source('../env.R')
Using GitHub PAT from the git credential store.
Skipping install of 'clootl' from a github remote, the SHA1 (1f190618) has not changed since last install.
  Use `force = TRUE` to force installation
community_data = read_csv(filename(COMMUNITY_OUTPUT_DIR, 'community_assembly_metrics_using_relative_abundance.csv'))
Rows: 308 Columns: 14── Column specification ────────────────────────────────────────────────
Delimiter: ","
dbl (14): mntd_standard, mntd_normal, mntd_actual, mass_fdiv_standar...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
head(community_data)
colnames(community_data)
 [1] "mntd_standard"            "mntd_normal"             
 [3] "mntd_actual"              "mass_fdiv_standard"      
 [5] "mass_fdiv_normal"         "mass_fdiv_actual"        
 [7] "beak_width_fdiv_standard" "beak_width_fdiv_normal"  
 [9] "beak_width_fdiv_actual"   "hwi_fdiv_standard"       
[11] "hwi_fdiv_normal"          "hwi_fdiv_actual"         
[13] "city_id"                  "urban_pool_size"         
min(community_data$mntd_standard)
[1] -2.338219
max(community_data$mntd_standard)
[1] 2.34743
min(community_data$beak_width_fdiv_standard)
[1] -2.685152
max(community_data$beak_width_fdiv_standard)
[1] 1.931681
min(community_data$hwi_fdiv_standard)
[1] -2.254896
max(community_data$hwi_fdiv_standard)
[1] 2.315362
min(community_data$mass_fdiv_standard)
[1] -2.351968
max(community_data$mass_fdiv_standard)
[1] 2.114779
min(community_data$mntd_normal)
[1] 0
max(community_data$mntd_normal)
[1] 1
min(community_data$beak_width_fdiv_normal)
[1] 0
max(community_data$beak_width_fdiv_normal)
[1] 1
min(community_data$hwi_fdiv_normal)
[1] 0
max(community_data$hwi_fdiv_normal)
[1] 1
min(community_data$mass_fdiv_normal)
[1] 0
max(community_data$mass_fdiv_normal)
[1] 1

Join on realms

city_to_realm = read_csv(filename(CITY_DATA_OUTPUT_DIR, 'realms.csv'))
Rows: 337 Columns: 2── Column specification ────────────────────────────────────────────────
Delimiter: ","
chr (1): core_realm
dbl (1): city_id
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
community_data_with_realm = left_join(community_data, city_to_realm)
Joining with `by = join_by(city_id)`

Cities as points

city_points = st_centroid(read_sf(filename(CITY_DATA_OUTPUT_DIR, 'city_selection.shp'))) %>% left_join(community_data_with_realm)
Warning: st_centroid assumes attributes are constant over geometriesWarning: st_centroid does not give correct centroids for longitude/latitude dataJoining with `by = join_by(city_id)`
city_points_coords = st_coordinates(city_points)
city_points$latitude = city_points_coords[,1]
city_points$longitude = city_points_coords[,2]
world_map = read_country_boundaries()

Load community data, and create long format version

communities = read_csv(filename(COMMUNITY_OUTPUT_DIR, 'communities_for_analysis.csv'))
Rows: 2428 Columns: 7── Column specification ────────────────────────────────────────────────
Delimiter: ","
chr (5): city_name, ebird_species_name, seasonal, presence, origin
dbl (2): city_id, relative_abundance_proxy
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
communities
community_summary = communities %>% group_by(city_id) %>% summarise(regional_pool_size = n(), urban_pool_size = sum(relative_abundance_proxy > 0))
community_summary

Load trait data

traits = read_csv(filename(TAXONOMY_OUTPUT_DIR, 'traits_ebird.csv'))
Rows: 332 Columns: 10── Column specification ────────────────────────────────────────────────
Delimiter: ","
chr (5): ebird_species_name, habitat, trophic_level, trophic_niche, ...
dbl (5): beak_width, hwi, mass, habitat_density, migration
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
head(traits)

Load spatial var

spatial_var = read_csv(filename(COMMUNITY_OUTPUT_DIR, 'spatial_var.csv')) %>% filter(city_id %in% community_summary$city_id)
Rows: 337 Columns: 3── Column specification ────────────────────────────────────────────────
Delimiter: ","
dbl (3): city_id, NMDS1, NMDS2
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
spatial_var

Summary metrics by Realm

test_required_values = function(name, df) {
  cat(paste(
    test_value_wilcox(paste(name, 'Std: MNTD'), df$mntd_standard),
    test_value_wilcox(paste(name, 'Std: Beak Width FDiv'), df$beak_width_fdiv_standard),
    test_value_wilcox(paste(name, 'Std: HWI FDiv'), df$hwi_fdiv_standard),
    test_value_wilcox(paste(name, 'Std: Mass FDiv'), df$mass_fdiv_standard),
    
    test_value_wilcox(paste(name, 'Norm: MNTD'), df$mntd_normal, mu = 0.5),
    test_value_wilcox(paste(name, 'Norm: Beak Width FDiv'), df$beak_width_fdiv_normal, mu = 0.5),
    test_value_wilcox(paste(name, 'Norm: HWI FDiv'), df$hwi_fdiv_normal, mu = 0.5),
    test_value_wilcox(paste(name, 'Norm: Mass FDiv'), df$mass_fdiv_normal, mu = 0.5),
    paste('N', nrow(df)),
    sep = "\n"))
}
test_required_values('Global', community_data_with_realm)
Global Std: MNTD median -0.36 ***
Global Std: Beak Width FDiv median 0.03 
Global Std: HWI FDiv median 0.39 **
Global Std: Mass FDiv median 0.29 ***
Global Norm: MNTD median 0.38 ***
Global Norm: Beak Width FDiv median 0.59 ***
Global Norm: HWI FDiv median 0.64 ***
Global Norm: Mass FDiv median 0.64 ***
N 308
unique(community_data_with_realm$core_realm)
[1] "Nearctic"    "Neotropic"   "Palearctic"  "Afrotropic" 
[5] "Indomalayan" "Australasia"
test_required_values('Nearctic', community_data_with_realm[community_data_with_realm$core_realm == 'Nearctic',])
Warning: cannot compute exact p-value with tiesWarning: cannot compute exact p-value with tiesWarning: cannot compute exact p-value with tiesWarning: cannot compute exact p-value with ties
Nearctic Std: MNTD median 0.67 *
Nearctic Std: Beak Width FDiv median 0.29 
Nearctic Std: HWI FDiv median -0.8 ***
Nearctic Std: Mass FDiv median -0.26 
Nearctic Norm: MNTD median 0.75 
Nearctic Norm: Beak Width FDiv median 0.62 
Nearctic Norm: HWI FDiv median 0.23 **
Nearctic Norm: Mass FDiv median 0.48 
N 46
test_required_values('Neotropic', community_data_with_realm[community_data_with_realm$core_realm == 'Neotropic',])
Neotropic Std: MNTD median 0.03 
Neotropic Std: Beak Width FDiv median -0.44 ***
Neotropic Std: HWI FDiv median -0.31 
Neotropic Std: Mass FDiv median 0.33 *
Neotropic Norm: MNTD median 0.45 
Neotropic Norm: Beak Width FDiv median 0.46 
Neotropic Norm: HWI FDiv median 0.5 
Neotropic Norm: Mass FDiv median 0.65 ***
N 64
test_required_values('Palearctic', community_data_with_realm[community_data_with_realm$core_realm == 'Palearctic',])
Palearctic Std: MNTD median 0.13 
Palearctic Std: Beak Width FDiv median 1.25 ***
Palearctic Std: HWI FDiv median -0.39 
Palearctic Std: Mass FDiv median 0.01 
Palearctic Norm: MNTD median 0.57 *
Palearctic Norm: Beak Width FDiv median 0.93 ***
Palearctic Norm: HWI FDiv median 0.38 
Palearctic Norm: Mass FDiv median 0.55 
N 72
test_required_values('Afrotropic', community_data_with_realm[community_data_with_realm$core_realm == 'Afrotropic',])
Afrotropic Std: MNTD median -1.27 *
Afrotropic Std: Beak Width FDiv median -0.5 
Afrotropic Std: HWI FDiv median 0.17 
Afrotropic Std: Mass FDiv median -0.95 
Afrotropic Norm: MNTD median 0.07 *
Afrotropic Norm: Beak Width FDiv median 0.42 
Afrotropic Norm: HWI FDiv median 0.54 
Afrotropic Norm: Mass FDiv median 0.34 
N 9
test_required_values('Indomalayan', community_data_with_realm[community_data_with_realm$core_realm == 'Indomalayan',])
Indomalayan Std: MNTD median -0.63 ***
Indomalayan Std: Beak Width FDiv median -0.7 ***
Indomalayan Std: HWI FDiv median 1.11 ***
Indomalayan Std: Mass FDiv median 0.84 ***
Indomalayan Norm: MNTD median 0.21 ***
Indomalayan Norm: Beak Width FDiv median 0.46 
Indomalayan Norm: HWI FDiv median 0.88 ***
Indomalayan Norm: Mass FDiv median 0.8 ***
N 111
test_required_values('Australasia', community_data_with_realm[community_data_with_realm$core_realm == 'Australasia',])
Australasia Std: MNTD median -1.39 
Australasia Std: Beak Width FDiv median -0.74 
Australasia Std: HWI FDiv median 0.78 
Australasia Std: Mass FDiv median -0.94 
Australasia Norm: MNTD median 0.09 
Australasia Norm: Beak Width FDiv median 0.46 
Australasia Norm: HWI FDiv median 0.79 
Australasia Norm: Mass FDiv median 0.55 
N 6

How significant are those differences:

print('Standard')
[1] "Standard"
kruskal.test(mntd_standard~core_realm, data = community_data_with_realm)

    Kruskal-Wallis rank sum test

data:  mntd_standard by core_realm
Kruskal-Wallis chi-squared = 102.11, df = 5, p-value <
0.00000000000000022
pairwise.wilcox.test(community_data_with_realm$mntd_standard, community_data_with_realm$core_realm)

    Pairwise comparisons using Wilcoxon rank sum exact test 

data:  community_data_with_realm$mntd_standard and community_data_with_realm$core_realm 

            Afrotropic      Australasia Indomalayan     Nearctic
Australasia 1.00000         -           -               -       
Indomalayan 0.00489         0.01024     -               -       
Nearctic    0.0000053220919 0.00018     0.0000000141740 -       
Neotropic   0.00018         0.00170     0.0000000083499 0.21142 
Palearctic  0.00018         0.00170     0.0000000000087 0.04492 
            Neotropic
Australasia -        
Indomalayan -        
Nearctic    -        
Neotropic   -        
Palearctic  1.00000  

P value adjustment method: holm 
print('Normal')
[1] "Normal"
kruskal.test(mntd_normal~core_realm, data = community_data_with_realm)

    Kruskal-Wallis rank sum test

data:  mntd_normal by core_realm
Kruskal-Wallis chi-squared = 122.2, df = 5, p-value <
0.00000000000000022
pairwise.wilcox.test(community_data_with_realm$mntd_normal, community_data_with_realm$core_realm)
Warning: cannot compute exact p-value with tiesWarning: cannot compute exact p-value with ties

    Pairwise comparisons using Wilcoxon rank sum exact test 

data:  community_data_with_realm$mntd_normal and community_data_with_realm$core_realm 

            Afrotropic      Australasia Indomalayan          Nearctic
Australasia 0.27213         -           -                    -       
Indomalayan 0.00013         0.00267     -                    -       
Nearctic    0.00276         0.01815     0.0000009331952      -       
Neotropic   0.0000199762545 0.00053     0.0000000000089      0.01638 
Palearctic  0.0000540321246 0.00147     < 0.0000000000000002 0.10733 
            Neotropic
Australasia -        
Indomalayan -        
Nearctic    -        
Neotropic   -        
Palearctic  0.00947  

P value adjustment method: holm 
print('Standard')
[1] "Standard"
kruskal.test(beak_width_fdiv_standard~core_realm, data = community_data_with_realm)

    Kruskal-Wallis rank sum test

data:  beak_width_fdiv_standard by core_realm
Kruskal-Wallis chi-squared = 107.95, df = 5, p-value <
0.00000000000000022
pairwise.wilcox.test(community_data_with_realm$beak_width_fdiv_standard, community_data_with_realm$core_realm)

    Pairwise comparisons using Wilcoxon rank sum exact test 

data:  community_data_with_realm$beak_width_fdiv_standard and community_data_with_realm$core_realm 

            Afrotropic Australasia Indomalayan         
Australasia 1.00000    -           -                   
Indomalayan 1.00000    1.00000     -                   
Nearctic    0.11361    1.00000     0.00123             
Neotropic   1.00000    1.00000     1.00000             
Palearctic  0.00026    0.13449     < 0.0000000000000002
            Nearctic          Neotropic        
Australasia -                 -                
Indomalayan -                 -                
Nearctic    -                 -                
Neotropic   0.00388           -                
Palearctic  0.000001461233727 0.000000000000018

P value adjustment method: holm 
print('Normal')
[1] "Normal"
kruskal.test(beak_width_fdiv_standard~core_realm, data = community_data_with_realm)

    Kruskal-Wallis rank sum test

data:  beak_width_fdiv_standard by core_realm
Kruskal-Wallis chi-squared = 107.95, df = 5, p-value <
0.00000000000000022
pairwise.wilcox.test(community_data_with_realm$beak_width_fdiv_standard, community_data_with_realm$core_realm)

    Pairwise comparisons using Wilcoxon rank sum exact test 

data:  community_data_with_realm$beak_width_fdiv_standard and community_data_with_realm$core_realm 

            Afrotropic Australasia Indomalayan         
Australasia 1.00000    -           -                   
Indomalayan 1.00000    1.00000     -                   
Nearctic    0.11361    1.00000     0.00123             
Neotropic   1.00000    1.00000     1.00000             
Palearctic  0.00026    0.13449     < 0.0000000000000002
            Nearctic          Neotropic        
Australasia -                 -                
Indomalayan -                 -                
Nearctic    -                 -                
Neotropic   0.00388           -                
Palearctic  0.000001461233727 0.000000000000018

P value adjustment method: holm 
print('Standard')
[1] "Standard"
kruskal.test(hwi_fdiv_standard~core_realm, data = community_data_with_realm)

    Kruskal-Wallis rank sum test

data:  hwi_fdiv_standard by core_realm
Kruskal-Wallis chi-squared = 115.62, df = 5, p-value <
0.00000000000000022
pairwise.wilcox.test(community_data_with_realm$hwi_fdiv_standard, community_data_with_realm$core_realm)

    Pairwise comparisons using Wilcoxon rank sum exact test 

data:  community_data_with_realm$hwi_fdiv_standard and community_data_with_realm$core_realm 

            Afrotropic Australasia Indomalayan         Nearctic
Australasia 1.0000     -           -                   -       
Indomalayan 0.1471     1.0000      -                   -       
Nearctic    1.0000     0.0038      0.00000000000000043 -       
Neotropic   1.0000     0.0680      0.00000000000000197 0.0925  
Palearctic  1.0000     0.3068      0.00000000003605628 0.2673  
            Neotropic
Australasia -        
Indomalayan -        
Nearctic    -        
Neotropic   -        
Palearctic  1.0000   

P value adjustment method: holm 
print('Normal')
[1] "Normal"
kruskal.test(hwi_fdiv_normal~core_realm, data = community_data_with_realm)

    Kruskal-Wallis rank sum test

data:  hwi_fdiv_normal by core_realm
Kruskal-Wallis chi-squared = 104.9, df = 5, p-value <
0.00000000000000022
pairwise.wilcox.test(community_data_with_realm$hwi_fdiv_normal, community_data_with_realm$core_realm)
Warning: cannot compute exact p-value with tiesWarning: cannot compute exact p-value with ties

    Pairwise comparisons using Wilcoxon rank sum exact test 

data:  community_data_with_realm$hwi_fdiv_normal and community_data_with_realm$core_realm 

            Afrotropic Australasia Indomalayan       
Australasia 0.7233     -           -                 
Indomalayan 0.1144     1.0000      -                 
Nearctic    0.0881     0.0193      0.0000000000006844
Neotropic   1.0000     0.0204      0.0000000000000014
Palearctic  0.9971     0.2748      0.0000000522948450
            Nearctic           Neotropic
Australasia -                  -        
Indomalayan -                  -        
Nearctic    -                  -        
Neotropic   0.0000964104748761 -        
Palearctic  0.0073             0.9953   

P value adjustment method: holm 
print('Standard')
[1] "Standard"
kruskal.test(mass_fdiv_standard~core_realm, data = community_data_with_realm)

    Kruskal-Wallis rank sum test

data:  mass_fdiv_standard by core_realm
Kruskal-Wallis chi-squared = 48.784, df = 5, p-value =
0.000000002457
pairwise.wilcox.test(community_data_with_realm$mass_fdiv_standard, community_data_with_realm$core_realm)

    Pairwise comparisons using Wilcoxon rank sum exact test 

data:  community_data_with_realm$mass_fdiv_standard and community_data_with_realm$core_realm 

            Afrotropic Australasia Indomalayan Nearctic Neotropic
Australasia 1.0000     -           -           -        -        
Indomalayan 0.0033     0.1401      -           -        -        
Nearctic    0.1401     1.0000      0.0023      -        -        
Neotropic   0.0158     0.2989      1.0000      0.1401   -        
Palearctic  0.0873     1.0000      0.00000002  1.0000   0.0658   

P value adjustment method: holm 
print('Normal')
[1] "Normal"
kruskal.test(mass_fdiv_normal~core_realm, data = community_data_with_realm)

    Kruskal-Wallis rank sum test

data:  mass_fdiv_normal by core_realm
Kruskal-Wallis chi-squared = 58.138, df = 5, p-value =
0.00000000002946
pairwise.wilcox.test(community_data_with_realm$mass_fdiv_normal, community_data_with_realm$core_realm)
Warning: cannot compute exact p-value with tiesWarning: cannot compute exact p-value with ties

    Pairwise comparisons using Wilcoxon rank sum exact test 

data:  community_data_with_realm$mass_fdiv_normal and community_data_with_realm$core_realm 

            Afrotropic Australasia Indomalayan     Nearctic Neotropic
Australasia 1.000      -           -               -        -        
Indomalayan 0.014      0.075       -               -        -        
Nearctic    1.000      1.000       0.016           -        -        
Neotropic   0.126      1.000       0.014           0.958    -        
Palearctic  0.824      1.000       0.0000000000008 1.000    0.014    

P value adjustment method: holm 

What families exist in which realms?

communities %>% 
  left_join(city_to_realm) %>% 
  mutate(family = gsub( " .*$", "", ebird_species_name)) %>%
  dplyr::select(family, core_realm) %>%
  distinct() %>%
  arrange(core_realm)
Joining with `by = join_by(city_id)`

Total urban families

communities %>% 
  mutate(family = gsub( " .*$", "", ebird_species_name)) %>%
  dplyr::select(family) %>%
  distinct() %>%
  arrange()

of which urban

communities %>% 
  filter(relative_abundance_proxy > 0) %>%
  mutate(family = gsub( " .*$", "", ebird_species_name)) %>%
  dplyr::select(family) %>%
  distinct() %>%
  arrange()

Summary metrics by introduced species

communities = read_csv(filename(COMMUNITY_OUTPUT_DIR, 'communities_for_analysis.csv'))
Rows: 2428 Columns: 7── Column specification ────────────────────────────────────────────────
Delimiter: ","
chr (5): city_name, ebird_species_name, seasonal, presence, origin
dbl (2): city_id, relative_abundance_proxy
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
city_introduced_species = communities %>% group_by(city_id) %>% summarise(number_of_species = n()) %>% left_join(
  communities %>% group_by(city_id) %>% filter(origin == 'Introduced') %>% summarise(number_of_introduced_species = n())
) %>% replace_na(list(number_of_introduced_species = 0))
Joining with `by = join_by(city_id)`
community_data_with_introductions = left_join(community_data, city_introduced_species)
Joining with `by = join_by(city_id)`
community_data_with_introductions$has_introduced_species = community_data_with_introductions$number_of_introduced_species > 0
community_data_with_introductions
communities %>% 
  filter(origin == 'Introduced') %>%
  dplyr::select(ebird_species_name) %>%
  group_by(ebird_species_name) %>%
  summarise(total_cities = n()) %>%
  arrange(desc(total_cities))
community_data_with_introductions[,c('mntd_standard', 'has_introduced_species')]
community_data_with_introductions %>% group_by(has_introduced_species) %>% summarise(
  total_cities = n(), 
  
  mean_mntd_std = mean(mntd_standard, na.rm = T),
  median_mntd_std = median(mntd_standard, na.rm = T),
  sd_mntd_std = sd(mntd_standard, na.rm = T),
  
  mean_mass_fdiv_std = mean(mass_fdiv_standard, na.rm = T),
  median_mass_fdiv_std = median(mass_fdiv_standard, na.rm = T),
  sd_mass_fdiv_std = sd(mass_fdiv_standard, na.rm = T),
  
  mean_gape_width_fdiv_std = mean(beak_width_fdiv_standard, na.rm = T),
  median_gape_width_fdiv_std = median(beak_width_fdiv_standard, na.rm = T),
  sd_gape_width_fdiv_std = sd(beak_width_fdiv_standard, na.rm = T),
  
  mean_handwing_index_fdiv_std = mean(hwi_fdiv_standard, na.rm = T),
  median_handwing_index_fdiv_std = median(hwi_fdiv_standard, na.rm = T),
  sd_handwing_index_fdiv_std = sd(hwi_fdiv_standard, na.rm = T)
)
community_data_with_introductions %>% group_by(has_introduced_species) %>% summarise(
  total_cities = n(), 
  
  median_mntd_nrm = median(mntd_normal, na.rm = T),
  median_mass_fdiv_nrm = median(mass_fdiv_normal, na.rm = T),
  median_gape_width_fdiv_nrm = median(beak_width_fdiv_normal, na.rm = T),
  median_handwing_index_fdiv_nrm = median(hwi_fdiv_normal, na.rm = T)
)
test_required_values('Has introduced', community_data_with_introductions[community_data_with_introductions$has_introduced_species == T,])
Has introduced Std: MNTD median -0.03 
Has introduced Std: Beak Width FDiv median 0.11 
Has introduced Std: HWI FDiv median -0.36 
Has introduced Std: Mass FDiv median 0.01 
Has introduced Norm: MNTD median 0.46 
Has introduced Norm: Beak Width FDiv median 0.6 ***
Has introduced Norm: HWI FDiv median 0.46 
Has introduced Norm: Mass FDiv median 0.58 **
N 189
test_required_values('Does not have introduced', community_data_with_introductions[community_data_with_introductions$has_introduced_species == F,])
Does not have introduced Std: MNTD median -0.54 ***
Does not have introduced Std: Beak Width FDiv median -0.28 *
Does not have introduced Std: HWI FDiv median 1.04 ***
Does not have introduced Std: Mass FDiv median 0.72 ***
Does not have introduced Norm: MNTD median 0.24 ***
Does not have introduced Norm: Beak Width FDiv median 0.54 
Does not have introduced Norm: HWI FDiv median 0.88 ***
Does not have introduced Norm: Mass FDiv median 0.79 ***
N 119

MNTD

ggplot(community_data_with_introductions, aes(x = has_introduced_species, y = mntd_standard)) + geom_boxplot()

ggplot(community_data_with_introductions, aes(x = has_introduced_species, y = mntd_normal)) + geom_boxplot()

wilcox.test(mntd_standard ~ has_introduced_species, community_data_with_introductions, na.action = 'na.omit')

    Wilcoxon rank sum test with continuity correction

data:  mntd_standard by has_introduced_species
W = 7922, p-value = 0.00001262
alternative hypothesis: true location shift is not equal to 0

There is a significant difference between the response of cities with introduced species (0.53±0.27) and those without (0.47±0.19) (p-value = 0.02).

wilcox.test(mntd_normal ~ has_introduced_species, community_data_with_introductions, na.action = 'na.omit')

    Wilcoxon rank sum test with continuity correction

data:  mntd_normal by has_introduced_species
W = 7376.5, p-value = 0.0000003705
alternative hypothesis: true location shift is not equal to 0

Mass FDiv

ggplot(community_data_with_introductions, aes(x = has_introduced_species, y = mass_fdiv_standard)) + geom_boxplot()

ggplot(community_data_with_introductions, aes(x = has_introduced_species, y = mass_fdiv_normal)) + geom_boxplot()

wilcox.test(mass_fdiv_standard ~ has_introduced_species, community_data_with_introductions, na.action = 'na.omit')

    Wilcoxon rank sum test with continuity correction

data:  mass_fdiv_standard by has_introduced_species
W = 15009, p-value = 0.0000007626
alternative hypothesis: true location shift is not equal to 0

There is a significant difference between the response of cities with introduced species (0.57±0.27) and those without (0.73±0.24) (p < 0.0001)

wilcox.test(mass_fdiv_normal ~ has_introduced_species, community_data_with_introductions, na.action = 'na.omit')

    Wilcoxon rank sum test with continuity correction

data:  mass_fdiv_normal by has_introduced_species
W = 15204, p-value = 0.0000001989
alternative hypothesis: true location shift is not equal to 0

Beak Gape FDiv

ggplot(community_data_with_introductions, aes(x = has_introduced_species, y = beak_width_fdiv_standard)) + geom_boxplot()

ggplot(community_data_with_introductions, aes(x = has_introduced_species, y = beak_width_fdiv_normal)) + geom_boxplot()

wilcox.test(beak_width_fdiv_standard ~ has_introduced_species, community_data_with_introductions, na.action = 'na.omit')

    Wilcoxon rank sum test with continuity correction

data:  beak_width_fdiv_standard by has_introduced_species
W = 8659, p-value = 0.0006786
alternative hypothesis: true location shift is not equal to 0

There is NOT a significant difference between the response of cities with introduced species (0.61±0.30) and those without (0.56±0.27)

wilcox.test(beak_width_fdiv_normal ~ has_introduced_species, community_data_with_introductions, na.action = 'na.omit')

    Wilcoxon rank sum test with continuity correction

data:  beak_width_fdiv_normal by has_introduced_species
W = 9829.5, p-value = 0.06288
alternative hypothesis: true location shift is not equal to 0

HWI FDiv

ggplot(community_data_with_introductions, aes(x = has_introduced_species, y = hwi_fdiv_standard)) + geom_boxplot()

ggplot(community_data_with_introductions, aes(x = has_introduced_species, y = hwi_fdiv_normal)) + geom_boxplot()

wilcox.test(hwi_fdiv_standard ~ has_introduced_species, community_data_with_introductions, na.action = 'na.omit')

    Wilcoxon rank sum test with continuity correction

data:  hwi_fdiv_standard by has_introduced_species
W = 17622, p-value < 0.00000000000000022
alternative hypothesis: true location shift is not equal to 0

There is a significant difference between the response of cities with introduced species (0.49±0.30) and those without (0.79±0.21) (p < 0.0001)

wilcox.test(hwi_fdiv_normal ~ has_introduced_species, community_data_with_introductions, na.action = 'na.omit')

    Wilcoxon rank sum test with continuity correction

data:  hwi_fdiv_normal by has_introduced_species
W = 17673, p-value < 0.00000000000000022
alternative hypothesis: true location shift is not equal to 0

What proportion of cities in each realm have introduced species

community_data_with_introductions %>% left_join(city_to_realm) %>%
  group_by(core_realm) %>%
  summarise(
    total_cities = n(), 
    total_cities_with_introduced = sum(has_introduced_species), 
    total_cities_without_introduced = n() - sum(has_introduced_species)) %>%
  arrange(core_realm)
Joining with `by = join_by(city_id)`

Are any introduced species not also present in a city?

communities %>% 
  filter(origin == 'Introduced') %>% 
  filter(relative_abundance_proxy < 0.1)

What’s the average relative abundance of introduced species compared to native

communities %>% 
  group_by(origin) %>% 
  summarise(average_relative_abundance = mean(relative_abundance_proxy))
communities %>% 
  group_by(origin) %>% 
  filter(relative_abundance_proxy > 0) %>%
  summarise(average_relative_abundance = mean(relative_abundance_proxy))
communities %>% 
  group_by(origin) %>% 
  summarise(average_relative_abundance = mean(relative_abundance_proxy))

Create analysis data frame for modelling

geography = read_csv(filename(CITY_DATA_OUTPUT_DIR, 'geography.csv'))
Rows: 342 Columns: 26── Column specification ────────────────────────────────────────────────
Delimiter: ","
dbl (26): city_id, city_avg_ndvi, city_avg_elevation, city_avg_temp,...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
names(geography)
 [1] "city_id"                       "city_avg_ndvi"                
 [3] "city_avg_elevation"            "city_avg_temp"                
 [5] "city_avg_min_monthly_temp"     "city_avg_max_monthly_temp"    
 [7] "city_avg_monthly_temp"         "city_avg_rainfall"            
 [9] "city_avg_max_monthly_rainfall" "city_avg_min_monthly_rainfall"
[11] "city_avg_soil_moisture"        "city_max_elev"                
[13] "city_min_elev"                 "city_elev_range"              
[15] "region_20km_avg_ndvi"          "region_20km_avg_elevation"    
[17] "region_20km_avg_soil_moisture" "region_20km_max_elev"         
[19] "region_20km_min_elev"          "region_20km_elev_range"       
[21] "region_50km_avg_ndvi"          "region_50km_avg_elevation"    
[23] "region_50km_avg_soil_moisture" "region_50km_max_elev"         
[25] "region_50km_min_elev"          "region_50km_elev_range"       
analysis_data = community_data_with_realm[,c('city_id', 
       'mntd_standard', 'mass_fdiv_standard', 'beak_width_fdiv_standard', 'hwi_fdiv_standard',
       'mntd_normal', 'mass_fdiv_normal', 'beak_width_fdiv_normal', 'hwi_fdiv_normal',
       'core_realm')] %>% 
  left_join(city_points[,c('city_id', 'latitude', 'longitude')]) %>%
  left_join(community_data_with_introductions[,c('city_id', 'has_introduced_species')]) %>%
  left_join(geography) %>%
  left_join(spatial_var)
Joining with `by = join_by(city_id)`Joining with `by = join_by(city_id)`Joining with `by = join_by(city_id)`Joining with `by = join_by(city_id)`
analysis_data$abs_latitude = abs(analysis_data$latitude)
analysis_data$core_realm = factor(analysis_data$core_realm, levels = c('Palearctic', 'Nearctic', 'Neotropic', 'Afrotropic', 'Indomalayan', 'Australasia', 'Oceania'))
analysis_data$has_introduced_species = factor(analysis_data$has_introduced_species, level = c('FALSE', 'TRUE'), labels = c('No introduced species', 'Introduced species'))
model_data = function(df, dependant_var) {
  df[,c(dependant_var, 'core_realm', 'abs_latitude', 'longitude', 'has_introduced_species', 'city_avg_ndvi', 'city_avg_elevation', 'city_avg_temp', 'city_avg_min_monthly_temp', 'city_avg_max_monthly_temp', 'city_avg_monthly_temp', 'city_avg_rainfall', 'city_avg_max_monthly_rainfall', 'city_avg_min_monthly_rainfall', 'city_avg_soil_moisture', 'city_max_elev', 'city_min_elev', 'city_elev_range', 'region_20km_avg_ndvi', 'region_20km_avg_elevation', 'region_20km_avg_soil_moisture', 'region_20km_max_elev', 'region_20km_min_elev', 'region_20km_elev_range', 'region_50km_avg_ndvi', 'region_50km_avg_elevation', 'region_50km_avg_soil_moisture', 'region_50km_max_elev', 'region_50km_min_elev', 'region_50km_elev_range')]
}
model_data(analysis_data, 'mntd_standard')
model_data(analysis_data, 'mntd_normal')
names(analysis_data)
 [1] "city_id"                       "mntd_standard"                
 [3] "mass_fdiv_standard"            "beak_width_fdiv_standard"     
 [5] "hwi_fdiv_standard"             "mntd_normal"                  
 [7] "mass_fdiv_normal"              "beak_width_fdiv_normal"       
 [9] "hwi_fdiv_normal"               "core_realm"                   
[11] "latitude"                      "longitude"                    
[13] "geometry"                      "has_introduced_species"       
[15] "city_avg_ndvi"                 "city_avg_elevation"           
[17] "city_avg_temp"                 "city_avg_min_monthly_temp"    
[19] "city_avg_max_monthly_temp"     "city_avg_monthly_temp"        
[21] "city_avg_rainfall"             "city_avg_max_monthly_rainfall"
[23] "city_avg_min_monthly_rainfall" "city_avg_soil_moisture"       
[25] "city_max_elev"                 "city_min_elev"                
[27] "city_elev_range"               "region_20km_avg_ndvi"         
[29] "region_20km_avg_elevation"     "region_20km_avg_soil_moisture"
[31] "region_20km_max_elev"          "region_20km_min_elev"         
[33] "region_20km_elev_range"        "region_50km_avg_ndvi"         
[35] "region_50km_avg_elevation"     "region_50km_avg_soil_moisture"
[37] "region_50km_max_elev"          "region_50km_min_elev"         
[39] "region_50km_elev_range"        "NMDS1"                        
[41] "NMDS2"                         "abs_latitude"                 

NMDS Spatial Helpers

analysis_data_nmds_coords = analysis_data[,c('NMDS1', 'NMDS2')]
coordinates(analysis_data_nmds_coords)  = ~ NMDS1 + NMDS2

analysis_data_nmds_nearneigh <- knearneigh(analysis_data_nmds_coords)
Warning: knearneigh: identical points foundWarning: knearneigh: kd_tree not available for identical points
analysis_data_nmds_neighbours <- knn2nb(analysis_data_nmds_nearneigh)
Warning: neighbour object has 76 sub-graphs

Polygons around realms in NMDS plot

cities_to_realms_nmds = read_csv(filename(CITY_DATA_OUTPUT_DIR, 'realms.csv')) %>% left_join(analysis_data) %>% filter(!is.na(NMDS1))
Rows: 337 Columns: 2── Column specification ────────────────────────────────────────────────
Delimiter: ","
chr (1): core_realm
dbl (1): city_id
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.Joining with `by = join_by(city_id, core_realm)`
unique(cities_to_realms_nmds$core_realm)
[1] "Nearctic"    "Neotropic"   "Palearctic"  "Afrotropic" 
[5] "Indomalayan" "Australasia"
realm_nmds_neartic_polygon = cities_to_realms_nmds %>% filter(core_realm == 'Nearctic') %>% slice(chull(NMDS1, NMDS2))
realm_nmds_neotropic_polygon = cities_to_realms_nmds %>% filter(core_realm == 'Neotropic') %>% slice(chull(NMDS1, NMDS2))
realm_nmds_palearctic_polygon = cities_to_realms_nmds %>% filter(core_realm == 'Palearctic') %>% slice(chull(NMDS1, NMDS2))
realm_nmds_afrotropic_polygon = cities_to_realms_nmds %>% filter(core_realm == 'Afrotropic') %>% slice(chull(NMDS1, NMDS2))
realm_nmds_indomalayan_polygon = cities_to_realms_nmds %>% filter(core_realm == 'Indomalayan') %>% slice(chull(NMDS1, NMDS2))
realm_nmds_australasia_polygon = cities_to_realms_nmds %>% filter(core_realm == 'Australasia') %>% slice(chull(NMDS1, NMDS2))

polygon_line_type = 'dashed'
polygon_linewidth = 0.4

with_realms_nmds = function(g) {
  g + 
    geom_polygon(data = realm_nmds_neartic_polygon, linewidth = polygon_linewidth, linetype = polygon_line_type, alpha = 0) +
    geom_polygon(data = realm_nmds_neotropic_polygon, linewidth = polygon_linewidth, linetype = polygon_line_type, alpha = 0) +
    geom_polygon(data = realm_nmds_palearctic_polygon, linewidth = polygon_linewidth, linetype = polygon_line_type, alpha = 0) +
    geom_polygon(data = realm_nmds_afrotropic_polygon, linewidth = polygon_linewidth, linetype = polygon_line_type, alpha = 0) +
    geom_polygon(data = realm_nmds_indomalayan_polygon, linewidth = polygon_linewidth, linetype = polygon_line_type, alpha = 0) +
    geom_polygon(data = realm_nmds_australasia_polygon, linewidth = polygon_linewidth, linetype = polygon_line_type, alpha = 0)
}

Lat Long Spatial Helpers

analysis_data_latlong_coords = analysis_data[,c('longitude', 'latitude')]
coordinates(analysis_data_latlong_coords)  = ~ longitude + latitude

analysis_data_coords_nearneigh <- knearneigh(analysis_data_latlong_coords, longlat = TRUE)
analysis_data_neighbours <- knn2nb(analysis_data_coords_nearneigh)
Warning: neighbour object has 97 sub-graphs

Polygons around realms in lat long plot

cities_to_realms_latlong = read_csv(filename(CITY_DATA_OUTPUT_DIR, 'realms.csv')) %>% left_join(analysis_data) %>% filter(!is.na(latitude))
Rows: 337 Columns: 2── Column specification ────────────────────────────────────────────────
Delimiter: ","
chr (1): core_realm
dbl (1): city_id
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.Joining with `by = join_by(city_id, core_realm)`
unique(cities_to_realms_latlong$core_realm)
[1] "Nearctic"    "Neotropic"   "Palearctic"  "Afrotropic" 
[5] "Indomalayan" "Australasia"
realm_latlong_neartic_polygon = cities_to_realms_latlong %>% filter(core_realm == 'Nearctic') %>% slice(chull(latitude, longitude))
realm_latlong_neotropic_polygon = cities_to_realms_latlong %>% filter(core_realm == 'Neotropic') %>% slice(chull(latitude, longitude))
realm_latlong_palearctic_polygon = cities_to_realms_latlong %>% filter(core_realm == 'Palearctic') %>% slice(chull(latitude, longitude))
realm_latlong_afrotropic_polygon = cities_to_realms_latlong %>% filter(core_realm == 'Afrotropic') %>% slice(chull(latitude, longitude))
realm_latlong_indomalayan_polygon = cities_to_realms_latlong %>% filter(core_realm == 'Indomalayan') %>% slice(chull(latitude, longitude))
realm_latlong_australasia_polygon = cities_to_realms_latlong %>% filter(core_realm == 'Australasia') %>% slice(chull(latitude, longitude))

with_realms_latlong = function(g) {
  g + 
    geom_polygon(data = realm_latlong_neartic_polygon, linewidth = polygon_linewidth, linetype = polygon_line_type, alpha = 0) +
    geom_polygon(data = realm_latlong_neotropic_polygon, linewidth = polygon_linewidth, linetype = polygon_line_type, alpha = 0) +
    geom_polygon(data = realm_latlong_palearctic_polygon, linewidth = polygon_linewidth, linetype = polygon_line_type, alpha = 0) +
    geom_polygon(data = realm_latlong_afrotropic_polygon, linewidth = polygon_linewidth, linetype = polygon_line_type, alpha = 0) +
    geom_polygon(data = realm_latlong_indomalayan_polygon, linewidth = polygon_linewidth, linetype = polygon_line_type, alpha = 0) +
    geom_polygon(data = realm_latlong_australasia_polygon, linewidth = polygon_linewidth, linetype = polygon_line_type, alpha = 0)
}

Check for spatial autocorrelation

MNTD

Lat/Long

with_realms_latlong(ggplot(analysis_data, aes(x = latitude, y = longitude, colour = mntd_standard)) + geom_point() + standardised_colours_scale + labs(colour = "Standardised response"))

moran.test(analysis_data$mntd_standard, nb2listw(analysis_data_neighbours))

    Moran I test under randomisation

data:  analysis_data$mntd_standard  
weights: nb2listw(analysis_data_neighbours)    

Moran I statistic standard deviate = 7.343, p-value =
0.0000000000001044
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.529792986      -0.003257329       0.005269694 
with_realms_latlong(ggplot(analysis_data, aes(x = latitude, y = longitude, colour = mntd_normal)) + geom_point() + normalised_colours_scale + labs(colour = "Normalised response"))

moran.test(analysis_data$mntd_normal, nb2listw(analysis_data_neighbours))

    Moran I test under randomisation

data:  analysis_data$mntd_normal  
weights: nb2listw(analysis_data_neighbours)    

Moran I statistic standard deviate = 7.6496, p-value =
0.00000000000001008
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.552360477      -0.003257329       0.005275586 

NMDS

with_realms_nmds(ggplot(analysis_data, aes(x = NMDS1, y = NMDS2, colour = mntd_standard)) + geom_point() + standardised_colours_scale + labs(colour = "Standardised response"))

moran.test(analysis_data$mntd_standard, nb2listw(analysis_data_nmds_neighbours))

    Moran I test under randomisation

data:  analysis_data$mntd_standard  
weights: nb2listw(analysis_data_nmds_neighbours)    

Moran I statistic standard deviate = 6.5193, p-value =
0.00000000003531
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.445538119      -0.003257329       0.004739028 
with_realms_nmds(ggplot(analysis_data, aes(x = NMDS1, y = NMDS2, colour = mntd_normal)) + geom_point() + standardised_colours_scale + labs(colour = "Normalised response"))

moran.test(analysis_data$mntd_normal, nb2listw(analysis_data_nmds_neighbours))

    Moran I test under randomisation

data:  analysis_data$mntd_normal  
weights: nb2listw(analysis_data_nmds_neighbours)    

Moran I statistic standard deviate = 6.2392, p-value =
0.0000000002199
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.426489674      -0.003257329       0.004744227 

FDiv Beak Width

Lat/Long

with_realms_latlong(ggplot(analysis_data, aes(x = latitude, y = longitude, colour = beak_width_fdiv_standard)) + geom_point() + standardised_colours_scale + labs(colour = "Standardised response"))

moran.test(analysis_data$beak_width_fdiv_standard, nb2listw(analysis_data_neighbours))

    Moran I test under randomisation

data:  analysis_data$beak_width_fdiv_standard  
weights: nb2listw(analysis_data_neighbours)    

Moran I statistic standard deviate = 7.7208, p-value =
0.000000000000005781
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.557818587      -0.003257329       0.005281064 
with_realms_latlong(ggplot(analysis_data, aes(x = latitude, y = longitude, colour = beak_width_fdiv_normal)) + geom_point() + standardised_colours_scale + labs(colour = "Normalised response"))

moran.test(analysis_data$beak_width_fdiv_normal, nb2listw(analysis_data_neighbours))

    Moran I test under randomisation

data:  analysis_data$beak_width_fdiv_normal  
weights: nb2listw(analysis_data_neighbours)    

Moran I statistic standard deviate = 8.3245, p-value <
0.00000000000000022
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.601742587      -0.003257329       0.005281967 

NMDS

with_realms_nmds(ggplot(analysis_data, aes(x = NMDS1, y = NMDS2, colour = beak_width_fdiv_standard)) + geom_point() + standardised_colours_scale + labs(colour = "Standardised response"))

moran.test(analysis_data$beak_width_fdiv_standard, nb2listw(analysis_data_nmds_neighbours))

    Moran I test under randomisation

data:  analysis_data$beak_width_fdiv_standard  
weights: nb2listw(analysis_data_nmds_neighbours)    

Moran I statistic standard deviate = 8.0587, p-value =
0.0000000000000003855
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.552096148      -0.003257329       0.004749061 
with_realms_nmds(ggplot(analysis_data, aes(x = NMDS1, y = NMDS2, colour = beak_width_fdiv_normal)) + geom_point() + standardised_colours_scale + labs(colour = "Normalised response"))

moran.test(analysis_data$beak_width_fdiv_normal, nb2listw(analysis_data_nmds_neighbours))

    Moran I test under randomisation

data:  analysis_data$beak_width_fdiv_normal  
weights: nb2listw(analysis_data_nmds_neighbours)    

Moran I statistic standard deviate = 7.5255, p-value =
0.00000000000002625
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.515397518      -0.003257329       0.004749858 

FDiv HWI

Lat/Long

with_realms_latlong(ggplot(analysis_data, aes(x = latitude, y = longitude, colour = hwi_fdiv_standard)) + geom_point() + standardised_colours_scale + labs(colour = "Standardised response"))

moran.test(analysis_data$hwi_fdiv_standard, nb2listw(analysis_data_neighbours))

    Moran I test under randomisation

data:  analysis_data$hwi_fdiv_standard  
weights: nb2listw(analysis_data_neighbours)    

Moran I statistic standard deviate = 9.756, p-value <
0.00000000000000022
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.705943030      -0.003257329       0.005284408 
with_realms_latlong(ggplot(analysis_data, aes(x = latitude, y = longitude, colour = hwi_fdiv_normal)) + geom_point() + standardised_colours_scale + labs(colour = "Normalised response"))

moran.test(analysis_data$hwi_fdiv_normal, nb2listw(analysis_data_neighbours))

    Moran I test under randomisation

data:  analysis_data$hwi_fdiv_normal  
weights: nb2listw(analysis_data_neighbours)    

Moran I statistic standard deviate = 10.186, p-value <
0.00000000000000022
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.737118550      -0.003257329       0.005283050 

NMDS

with_realms_nmds(ggplot(analysis_data, aes(x = NMDS1, y = NMDS2, colour = hwi_fdiv_standard)) + geom_point() + standardised_colours_scale + labs(colour = "Standardised response"))

moran.test(analysis_data$hwi_fdiv_standard, nb2listw(analysis_data_nmds_neighbours))

    Moran I test under randomisation

data:  analysis_data$hwi_fdiv_standard  
weights: nb2listw(analysis_data_nmds_neighbours)    

Moran I statistic standard deviate = 5.7294, p-value =
0.000000005039
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.391697713      -0.003257329       0.004752012 
with_realms_nmds(ggplot(analysis_data, aes(x = NMDS1, y = NMDS2, colour = hwi_fdiv_normal)) + geom_point() + standardised_colours_scale + labs(colour = "Normalised response"))

moran.test(analysis_data$hwi_fdiv_normal, nb2listw(analysis_data_nmds_neighbours))

    Moran I test under randomisation

data:  analysis_data$hwi_fdiv_normal  
weights: nb2listw(analysis_data_nmds_neighbours)    

Moran I statistic standard deviate = 6.2803, p-value =
0.0000000001689
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.429622019      -0.003257329       0.004750814 

FDiv Mass

Lat/Long

with_realms_latlong(ggplot(analysis_data, aes(x = latitude, y = longitude, colour = mass_fdiv_standard)) + geom_point() + standardised_colours_scale + labs(colour = "Standardised response"))

moran.test(analysis_data$mass_fdiv_standard, nb2listw(analysis_data_neighbours))

    Moran I test under randomisation

data:  analysis_data$mass_fdiv_standard  
weights: nb2listw(analysis_data_neighbours)    

Moran I statistic standard deviate = 9.1521, p-value <
0.00000000000000022
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.661355126      -0.003257329       0.005273416 
with_realms_latlong(ggplot(analysis_data, aes(x = latitude, y = longitude, colour = mass_fdiv_normal)) + geom_point() + standardised_colours_scale + labs(colour = "Normalised response"))

moran.test(analysis_data$mass_fdiv_normal, nb2listw(analysis_data_neighbours))

    Moran I test under randomisation

data:  analysis_data$mass_fdiv_normal  
weights: nb2listw(analysis_data_neighbours)    

Moran I statistic standard deviate = 9.008, p-value <
0.00000000000000022
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.650776207      -0.003257329       0.005271668 

NMDS

with_realms_nmds(ggplot(analysis_data, aes(x = NMDS1, y = NMDS2, colour = mass_fdiv_standard)) + geom_point() + standardised_colours_scale + labs(colour = "Standardised response"))

moran.test(analysis_data$mass_fdiv_standard, nb2listw(analysis_data_nmds_neighbours))

    Moran I test under randomisation

data:  analysis_data$mass_fdiv_standard  
weights: nb2listw(analysis_data_nmds_neighbours)    

Moran I statistic standard deviate = 6.2114, p-value =
0.0000000002625
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.424488243      -0.003257329       0.004742312 
with_realms_nmds(ggplot(analysis_data, aes(x = NMDS1, y = NMDS2, colour = mass_fdiv_normal)) + geom_point() + standardised_colours_scale + labs(colour = "Normalised response"))

moran.test(analysis_data$mass_fdiv_normal, nb2listw(analysis_data_nmds_neighbours))

    Moran I test under randomisation

data:  analysis_data$mass_fdiv_normal  
weights: nb2listw(analysis_data_nmds_neighbours)    

Moran I statistic standard deviate = 4.6538, p-value =
0.000001629
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.317170749      -0.003257329       0.004740769 

Examine individual metrics

all_explanatories = c(
    'city_avg_ndvi', 'city_avg_elevation', 'city_avg_temp',
    'region_50km_avg_soil_moisture',
    'core_realmAfrotropic', 'core_realmAustralasia', 'core_realmIndomalayan', 'core_realmNearctic', 'core_realmNeotropic', 'core_realmPalearctic',
    'has_introduced_speciesNo introduced species', 'has_introduced_speciesIntroduced species',
    'core_realmAfrotropic:has_introduced_speciesIntroduced species',
    'core_realmAustralasia:has_introduced_speciesIntroduced species',
    'core_realmIndomalayan:has_introduced_speciesIntroduced species',
    'core_realmNearctic:has_introduced_speciesIntroduced species',
    'core_realmNeotropic:has_introduced_speciesIntroduced species',
    'core_realmPalearctic:has_introduced_speciesIntroduced species',
    'core_realmOceania:has_introduced_speciesIntroduced species'
)

all_explanatory_names = factor(
   c(
    'Avg. NDVI', 'Avg. Elevation', 'Avg. Temp.',
    'Avg. Soil Moisture',
    'Afrotropic', 'Australasia', 'Indomalayan', 'Nearctic', 'Neotropic', 'Palearctic',
    'Introduced Absent', 'Introduced Present',
    'Afrotropical * Introduced present',
    'Austaliasian * Introduced present',
    'Indomalayan * Introduced present',
    'Nearctic * Introduced present',
    'Neotropical * Introduced present',
    'Palearctic * Introduced present',
    'Oceanical * Introduced present'
  ), ordered = T
)

explanatory_dictionary = data.frame(explanatory = all_explanatories, explanatory_name = all_explanatory_names)
  
with_explanatory_type_labels = function(p) {
  p = p[p$explanatory != '(Intercept)',]
  explanatory_levels = all_explanatories[all_explanatories %in% p$explanatory]
  p$explanatory <- factor(p$explanatory, levels = explanatory_levels)
  
  p$type <- 'Realm'
  p$type[p$explanatory %in% c('city_avg_ndvi', 'city_avg_elevation', 'city_avg_temp')] <- 'City geography'
  p$type[p$explanatory %in% c('region_50km_avg_soil_moisture')] <- 'Regional (50 km) geography'
  p$type[p$explanatory %in% c('has_introduced_speciesNo introduced species', 'has_introduced_speciesIntroduced species')] <- 'Introduced species'
  p
}

with_explanatory_names = function(p) {
  p %>% left_join(explanatory_dictionary) %>% arrange(desc(explanatory_name))
}

type_labels = function(p) {
  explanatory_levels = all_explanatories[all_explanatories %in% p$explanatory]
  p$explanatory <- factor(p$explanatory, levels = explanatory_levels)
  
  p$type <- 'Realm'
  p$type[p$explanatory %in% c('city_avg_ndvi', 'city_avg_elevation', 'city_avg_temp', 'city_avg_min_monthly_temp', 'city_avg_max_monthly_temp', 
    'city_avg_monthly_temp', 'city_avg_rainfall', 'city_avg_max_monthly_rainfall', 'city_avg_min_monthly_rainfall', 
    'city_avg_soil_moisture', 'city_max_elev', 'city_min_elev', 'city_elev_range')] <- 'City geography'
  p$type[p$explanatory %in% c('region_50km_avg_ndvi', 'region_50km_avg_elevation', 'region_50km_avg_soil_moisture', 'region_50km_max_elev', 
    'region_50km_min_elev', 'region_50km_elev_range')] <- 'Regional (50 km) geography'
   p$type[p$explanatory %in% c('region_20km_avg_ndvi', 'region_20km_avg_elevation', 'region_20km_avg_soil_moisture', 'region_20km_max_elev', 
    'region_20km_min_elev', 'region_20km_elev_range')] <- 'Regional (20 km) geography'
  p$type[p$explanatory %in% c('has_introduced_speciesNo introduced species', 'has_introduced_speciesIntroduced species')] <- 'Introduced species'
  p
}
explanatory_labels = c(
  'has_introduced_species'='Introduced species', 
  'has_introduced_speciesNo introduced species'='Introduced absent', 
  'has_introduced_speciesIntroduced species'='Introduced present',
  'city_avg_ndvi'='Average NDVI', 
  'city_avg_elevation'='Average elevation', 
  'city_avg_temp'='Average temperature', 
  'city_avg_min_monthly_temp'='Average minimum monthly temperature', 
  'city_avg_max_monthly_temp'='Average maximum monthly temperature', 
  'city_avg_monthly_temp'='Average monthly temperature', 
  'city_avg_rainfall'='Average rainfall', 
  'city_avg_max_monthly_rainfall'='Average maximum monthly rainfall', 
  'city_avg_min_monthly_rainfall'='Average minimum monthly rainfall', 
  'city_avg_soil_moisture'='Average soil moisture', 
  'city_max_elev'='Maximum elevation', 
  'city_min_elev'='Minimum elevation', 
  'city_elev_range'='Elevation range', 
  'region_20km_avg_ndvi'='Average NDVI', 
  'region_20km_avg_elevation'='Average elevation', 
  'region_20km_avg_soil_moisture'='Average soil moisture', 
  'region_20km_max_elev'='Maximum elevation', 
  'region_20km_min_elev'='Minimum elevation',
  'region_20km_elev_range'='Elevation range',
  'region_50km_avg_ndvi'='Average NDVI',
  'region_50km_avg_elevation'='Average elevation',
  'region_50km_avg_soil_moisture'='Average soil moisture', 
  'region_50km_max_elev'='Maximum elevation',
  'region_50km_min_elev'='Minimum elevation', 
  'region_50km_elev_range'='Elevation range',
  'abs_latitude' = 'Absolute latitude',
  'latitude' = 'Latitude',
  'longitude' = 'Longitude',
  'core_realmAfrotropic' = 'Afrotropical', 
  'core_realmAustralasia' = 'Austaliasian', 
  'core_realmIndomalayan' = 'Indomalayan', 
  'core_realmNearctic' = 'Nearctic', 
  'core_realmNeotropic' = 'Neotropical',
  'core_realmPalearctic' = 'Palearctic',
  'core_realmOceania' = 'Oceanical',
  'core_realmAfrotropic:has_introduced_speciesIntroduced species' = 'Afrotropical * Introduced present',
  'core_realmAustralasia:has_introduced_speciesIntroduced species' = 'Austaliasian * Introduced present',
  'core_realmIndomalayan:has_introduced_speciesIntroduced species' = 'Indomalayan * Introduced present',
  'core_realmNearctic:has_introduced_speciesIntroduced species' = 'Nearctic * Introduced present',
  'core_realmNeotropic:has_introduced_speciesIntroduced species' = 'Neotropical * Introduced present',
  'core_realmPalearctic:has_introduced_speciesIntroduced species' = 'Palearctic * Introduced present',
  'core_realmOceania:has_introduced_speciesIntroduced species' = 'Oceanical * Introduced present'
)
create_formula = function(response_var) {
  as.formula(paste(response_var, '~ core_realm + city_avg_ndvi + city_avg_elevation + city_avg_temp + region_50km_avg_soil_moisture + has_introduced_species'))
}

Helper plot functions

geom_map = function(map_sf, title, scale = standardised_colours_scale, colour_label = 'Standardised\nResponse') {
  norm_mntd_analysis_geo = ggplot() + 
    geom_sf(data = world_map, aes(geometry = geometry)) +
    map_sf +
    scale +
    labs(colour = colour_label) +
    theme_bw() +
    theme(legend.position="bottom")
}

geom_map_std = function(map_sf, title) {
  geom_map(map_sf, title)
}

geom_map_nrm = function(map_sf, title) {
  geom_map(map_sf, title, normalised_colours_scale, 'Normalised\nResponse')
}

Helper Dredge functions

# Taken from MuMIN package
# https://rdrr.io/cran/MuMIn/src/R/averaging.R
# https://rdrr.io/cran/MuMIn/src/R/model.avg.R

.coefarr.avg <-
  function(cfarr, weight, revised.var, full, alpha) {   
    weight <- weight / sum(weight)
    nCoef <- dim(cfarr)[3L]
    if(full) {
      nas <- is.na(cfarr[, 1L, ]) & is.na(cfarr[, 2L, ])
      cfarr[, 1L, ][nas] <- cfarr[, 2L, ][nas] <- 0
      #cfarr[, 1L:2L, ][is.na(cfarr[, 1L:2L, ])] <- 0
      if(!all(is.na(cfarr[, 3L, ])))
        cfarr[ ,3L, ][is.na(cfarr[ , 3L, ])] <- Inf
    }
    
    avgcoef <- array(dim = c(nCoef, 5L),
                     dimnames = list(dimnames(cfarr)[[3L]], c("Estimate",
                                                              "Std. Error", "Adjusted SE", "Lower CI", "Upper CI")))
    for(i in seq_len(nCoef))
      avgcoef[i, ] <- par.avg(cfarr[, 1L, i], cfarr[, 2L, i], weight,
                              df = cfarr[, 3L, i], alpha = alpha, revised.var = revised.var)
    
    avgcoef[is.nan(avgcoef)] <- NA
    return(avgcoef)
  }

.makecoefmat <- function(cf) {
  no.ase <- all(is.na(cf[, 3L]))
  z <- abs(cf[, 1L] / cf[, if(no.ase) 2L else 3L])
  pval <- 2 * pnorm(z, lower.tail = FALSE)
  cbind(cf[, if(no.ase) 1L:2L else 1L:3L, drop = FALSE],
        `z value` = z, `Pr(>|z|)` = zapsmall(pval))
}

# Generate model selections using lmer, dredge, and model.avg
# `forumla` : a two-sided linear formula object describing both the fixed-effects and random-effects part of the model
# `data` : the data frame containing the variables from the formula
# `aic_delta` : the AIC delta to use for selecting models in model average
model_average <- function(formula, data, aic_delta = 20) {
  model <- lm(
    formula,
    data=data
  )
  dredge_result <- dredge(model)
  summary(model.avg(dredge_result, subset = delta < aic_delta))
}

# Create a summary data frame containing the selected variables from a model
# `model_sum` : The model summary output from `model_average`
model_summary <- function(model_sum) {
  .column_name <- function(postfix) {
    postfix
  }
  
  # just return the estimate and p value
  weight <- model_sum$msTable[, 5L]
  
  coefmat.full <- as.data.frame(.makecoefmat(.coefarr.avg(model_sum$coefArray, weight,
                                                          attr(model_sum, "revised.var"), TRUE, 0.05)))
  
  coefmat.subset <-
    as.data.frame(.makecoefmat(.coefarr.avg(model_sum$coefArray, weight,
                                            attr(model_sum, "revised.var"), FALSE, 0.05)))
  
  
  coefmat.subset <- coefmat.subset[-c(1), c(1, 2, 5)]
  names(coefmat.subset) <- c(.column_name("estimate"), .column_name("error"), .column_name("p"))
  coefmat.subset <- tibble::rownames_to_column(coefmat.subset, "explanatory")
  coefmat.subset$model = 'subset'
  
  coefmat.full <- coefmat.full[-c(1), c(1, 2, 5)]
  names(coefmat.full) <- c(.column_name("estimate"), .column_name("error"), .column_name("p"))
  coefmat.full <- tibble::rownames_to_column(coefmat.full, "explanatory")
  coefmat.full$model = 'full'
  
  rbind(coefmat.full, coefmat.subset)
}
plot_dredge_result = function(result_table, mu = 0) {
  p = result_table[result_table$model == 'full',]
  p = type_labels(p)

  ggplot(p, aes(y = explanatory, x = estimate, colour = type)) + 
    geom_line() +
    geom_point() +
    geom_errorbar(aes(xmin=estimate-error, xmax=estimate+error), width=.2,
                   position=position_dodge(0.05)) +
    scale_y_discrete(
      limits = rev(levels(p$explanatory)), 
      labels = explanatory_labels) +
    scale_colour_manual(
      values = c(realm_colour, city_geography_colour, regional_50km_geography_colour, regional_20km_geography_colour, introduced_species_colour), 
      breaks = c('Realm', 'City geography', 'Regional (50 km) geography', 'Regional (20 km) geography', 'Introduced species')) +
    theme_bw() +
    geom_vline(xintercept=mu, linetype="dotted") +
    guides(colour=guide_legend(title="Predictor type")) + xlab('Difference in response from 0\nhabitat filtering (< 0) and competitive interactions (> 0)\n± Standard Error') + ylab('Predictor') +
    theme(legend.justification = "top")
}

GLS Spatial Helpers

gls_method = "ML"

spatial_model = function(formula, correlation) {
  gls(
    formula, 
    data = analysis_data, 
    correlation = correlation, 
    method = gls_method
  )
}

plot_spatial_result = function(model_result) {
  model_summary = summary(model_result)
  result_table = as.data.frame(model_summary$tTable)
  result_table$explanatory = rownames(result_table)
  
  result_table = result_table %>% with_explanatory_type_labels() %>% with_explanatory_names()
  
  ggplot2::ggplot(result_table, ggplot2::aes(y=factor(explanatory_name, level = all_explanatory_names, ordered = T), x=Value, colour = type)) + 
    ggplot2::geom_line() +
    ggplot2::geom_point() +
    ggplot2::geom_errorbar(ggplot2::aes(xmin=Value-Std.Error, xmax=Value+Std.Error), width=.2,
                   position=ggplot2::position_dodge(0.05)) +
    ggplot2::theme_bw() +
    ggplot2::geom_vline(xintercept=0, linetype="dotted") +
    ggplot2::theme(legend.justification = "top") +
    ylab('Predictor') +
    guides(colour=guide_legend(title="Predictor type")) + xlab('Difference in response from 0\nhabitat filtering (< 0) and competitive interactions (> 0)\n± Standard Error') +
    scale_colour_manual(
      values = c(realm_colour, city_geography_colour, regional_50km_geography_colour, introduced_species_colour), 
      breaks = c('Realm', 'City geography', 'Regional (50 km) geography', 'Introduced species')) +
    scale_y_discrete(limits = rev(all_explanatory_names[all_explanatory_names %in% result_table$explanatory_name]))
}

Choose best spatial correlation function

MNTD

AIC(spatial_model(create_formula('mntd_standard'), corLin(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] 661.8901
AIC(spatial_model(create_formula('mntd_standard'), corLin(form = ~ latitude + longitude)))
[1] 666.4295
AIC(spatial_model(create_formula('mntd_standard'), corExp(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] 653.0099
AIC(spatial_model(create_formula('mntd_standard'), corExp(form = ~ latitude + longitude)))
[1] 656.6118
AIC(spatial_model(create_formula('mntd_standard'), corGaus(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] 657.7216
AIC(spatial_model(create_formula('mntd_standard'), corGaus(form = ~ latitude + longitude)))
[1] 662.5602
AIC(spatial_model(create_formula('mntd_standard'), corRatio(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] 650.3759
AIC(spatial_model(create_formula('mntd_standard'), corRatio(form = ~ latitude + longitude)))
[1] 654.8339
AIC(spatial_model(create_formula('mntd_standard'), corSpher(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] 659.8723
AIC(spatial_model(create_formula('mntd_standard'), corSpher(form = ~ latitude + longitude)))
[1] 664.1159

MNTD: corRatio with NMDS + lat/long

AIC(spatial_model(create_formula('mntd_normal'), corLin(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] -81.16061
#AIC(spatial_model(create_formula('mntd_normal'), corLin(form = ~ latitude + longitude)))
AIC(spatial_model(create_formula('mntd_normal'), corExp(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] -89.28852
AIC(spatial_model(create_formula('mntd_normal'), corExp(form = ~ latitude + longitude)))
[1] -86.17138
AIC(spatial_model(create_formula('mntd_normal'), corGaus(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] -86.02288
AIC(spatial_model(create_formula('mntd_normal'), corGaus(form = ~ latitude + longitude)))
[1] -79.92235
AIC(spatial_model(create_formula('mntd_normal'), corRatio(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] -92.46759
AIC(spatial_model(create_formula('mntd_normal'), corRatio(form = ~ latitude + longitude)))
[1] -86.60443
AIC(spatial_model(create_formula('mntd_normal'), corSpher(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] -86.78558
AIC(spatial_model(create_formula('mntd_normal'), corSpher(form = ~ latitude + longitude)))
[1] -81.47393

Beak Width

AIC(spatial_model(create_formula('beak_width_fdiv_standard'), corLin(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] 793.5251
#AIC(spatial_model(create_formula('beak_width_fdiv_standard'), corLin(form = ~ latitude + longitude)))
AIC(spatial_model(create_formula('beak_width_fdiv_standard'), corExp(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] 789.282
AIC(spatial_model(create_formula('beak_width_fdiv_standard'), corExp(form = ~ latitude + longitude)))
[1] 790.0085
AIC(spatial_model(create_formula('beak_width_fdiv_standard'), corGaus(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] 793.7668
AIC(spatial_model(create_formula('beak_width_fdiv_standard'), corGaus(form = ~ latitude + longitude)))
[1] 793.7656
AIC(spatial_model(create_formula('beak_width_fdiv_standard'), corRatio(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] 791.2572
AIC(spatial_model(create_formula('beak_width_fdiv_standard'), corRatio(form = ~ latitude + longitude)))
[1] 791.3899
AIC(spatial_model(create_formula('beak_width_fdiv_standard'), corSpher(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] 793.8106
AIC(spatial_model(create_formula('beak_width_fdiv_standard'), corSpher(form = ~ latitude + longitude)))
[1] 793.8106

Beak width: corExp with NMDS + lat/long

AIC(spatial_model(create_formula('beak_width_fdiv_normal'), corLin(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] -45.30823
AIC(spatial_model(create_formula('beak_width_fdiv_normal'), corLin(form = ~ latitude + longitude)))
[1] -45.30823
AIC(spatial_model(create_formula('beak_width_fdiv_normal'), corExp(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] -51.4648
AIC(spatial_model(create_formula('beak_width_fdiv_normal'), corExp(form = ~ latitude + longitude)))
[1] -50.46273
AIC(spatial_model(create_formula('beak_width_fdiv_normal'), corGaus(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] -45.13365
AIC(spatial_model(create_formula('beak_width_fdiv_normal'), corGaus(form = ~ latitude + longitude)))
[1] -45.13552
AIC(spatial_model(create_formula('beak_width_fdiv_normal'), corRatio(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] -47.44015
AIC(spatial_model(create_formula('beak_width_fdiv_normal'), corRatio(form = ~ latitude + longitude)))
[1] -47.368
AIC(spatial_model(create_formula('beak_width_fdiv_normal'), corSpher(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] -44.97422
AIC(spatial_model(create_formula('beak_width_fdiv_normal'), corSpher(form = ~ latitude + longitude)))
[1] -44.97475

HWI

AIC(spatial_model(create_formula('hwi_fdiv_standard'), corLin(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] 694.55
AIC(spatial_model(create_formula('hwi_fdiv_standard'), corLin(form = ~ latitude + longitude)))
[1] 699.3174
AIC(spatial_model(create_formula('hwi_fdiv_standard'), corExp(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] 666.6631
AIC(spatial_model(create_formula('hwi_fdiv_standard'), corExp(form = ~ latitude + longitude)))
[1] 671.4924
AIC(spatial_model(create_formula('hwi_fdiv_standard'), corGaus(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] 704.4854
AIC(spatial_model(create_formula('hwi_fdiv_standard'), corGaus(form = ~ latitude + longitude)))
[1] 709.2567
AIC(spatial_model(create_formula('hwi_fdiv_standard'), corRatio(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] 685.5013
AIC(spatial_model(create_formula('hwi_fdiv_standard'), corRatio(form = ~ latitude + longitude)))
[1] 690.9801
AIC(spatial_model(create_formula('hwi_fdiv_standard'), corSpher(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] 689.5034
AIC(spatial_model(create_formula('hwi_fdiv_standard'), corSpher(form = ~ latitude + longitude)))
[1] 707.7108

HWI: corExp with NMDS + lat/long

#AIC(spatial_model(create_formula('hwi_fdiv_normal'), corLin(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
#AIC(spatial_model(create_formula('hwi_fdiv_normal'), corLin(form = ~ latitude + longitude)))
AIC(spatial_model(create_formula('hwi_fdiv_normal'), corExp(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] -96.05945
AIC(spatial_model(create_formula('hwi_fdiv_normal'), corExp(form = ~ latitude + longitude)))
[1] -91.16342
AIC(spatial_model(create_formula('hwi_fdiv_normal'), corGaus(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] -54.57955
AIC(spatial_model(create_formula('hwi_fdiv_normal'), corGaus(form = ~ latitude + longitude)))
[1] -50.52492
AIC(spatial_model(create_formula('hwi_fdiv_normal'), corRatio(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] -74.90875
AIC(spatial_model(create_formula('hwi_fdiv_normal'), corRatio(form = ~ latitude + longitude)))
[1] -69.65899
AIC(spatial_model(create_formula('hwi_fdiv_normal'), corSpher(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] -53.60829
AIC(spatial_model(create_formula('hwi_fdiv_normal'), corSpher(form = ~ latitude + longitude)))
[1] -61.05117

Mass

AIC(spatial_model(create_formula('mass_fdiv_standard'), corLin(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] 703.1192
#AIC(spatial_model(create_formula('mass_fdiv_standard'), corLin(form = ~ latitude + longitude)))
AIC(spatial_model(create_formula('mass_fdiv_standard'), corExp(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] 753.3038
AIC(spatial_model(create_formula('mass_fdiv_standard'), corExp(form = ~ latitude + longitude)))
[1] 755.7536
AIC(spatial_model(create_formula('mass_fdiv_standard'), corGaus(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] 786.6415
AIC(spatial_model(create_formula('mass_fdiv_standard'), corGaus(form = ~ latitude + longitude)))
[1] 785.0663
AIC(spatial_model(create_formula('mass_fdiv_standard'), corRatio(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] 766.9612
AIC(spatial_model(create_formula('mass_fdiv_standard'), corRatio(form = ~ latitude + longitude)))
[1] 766.903
AIC(spatial_model(create_formula('mass_fdiv_standard'), corSpher(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] 780.4268
AIC(spatial_model(create_formula('mass_fdiv_standard'), corSpher(form = ~ latitude + longitude)))
[1] 779.8638

Mass: corExp with NMDS + lat/long

#AIC(spatial_model(create_formula('mass_fdiv_normal'), corLin(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
#AIC(spatial_model(create_formula('mass_fdiv_normal'), corLin(form = ~ latitude + longitude)))
AIC(spatial_model(create_formula('mass_fdiv_normal'), corExp(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] -49.779
AIC(spatial_model(create_formula('mass_fdiv_normal'), corExp(form = ~ latitude + longitude)))
[1] -48.0529
AIC(spatial_model(create_formula('mass_fdiv_normal'), corGaus(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] -21.84722
AIC(spatial_model(create_formula('mass_fdiv_normal'), corGaus(form = ~ latitude + longitude)))
[1] -23.29214
AIC(spatial_model(create_formula('mass_fdiv_normal'), corRatio(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] -35.17179
AIC(spatial_model(create_formula('mass_fdiv_normal'), corRatio(form = ~ latitude + longitude)))
[1] -35.60617
AIC(spatial_model(create_formula('mass_fdiv_normal'), corSpher(form = ~ NMDS1 + NMDS2 + latitude + longitude)))
[1] -27.00252
AIC(spatial_model(create_formula('mass_fdiv_normal'), corSpher(form = ~ latitude + longitude)))
[1] -27.91974
correlation_formula = as.formula('~ NMDS1 + NMDS2 + latitude + longitude')

correlation_function_fdiv = function() {
  corExp(form = correlation_formula)
}

correlation_function_mntd = function() {
  corRatio(form = correlation_formula)
}

MNTD

Standardised

std_mntd_analysis_geo_plot = geom_map_std(geom_sf(data = analysis_data, aes(color = mntd_standard, geometry = geometry)), 'MNTD')
std_mntd_analysis_geo_plot

std_mntd_analysis_data = model_data(analysis_data[!is.na(analysis_data$mntd_standard),], 'mntd_standard')
std_mntd_analysis_formula = create_formula('mntd_standard')
std_mntd_analysis_result = model_average(std_mntd_analysis_formula, std_mntd_analysis_data)
Fixed term is "(Intercept)"
std_mntd_analysis_result_table = model_summary(std_mntd_analysis_result)
std_mntd_analysis_result_table
std_mntd_analysis_pred_plot = plot_dredge_result(std_mntd_analysis_result_table)
std_mntd_analysis_pred_plot

Do the residuals still contain spatial autocorrelation from a fitted lm?

std_mntd_lm = lm(std_mntd_analysis_formula, std_mntd_analysis_data)
moran.test(std_mntd_lm$residuals, nb2listw(analysis_data_neighbours))

    Moran I test under randomisation

data:  std_mntd_lm$residuals  
weights: nb2listw(analysis_data_neighbours)    

Moran I statistic standard deviate = 3.8425, p-value =
0.0000609
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.275671194      -0.003257329       0.005269438 
moran.test(std_mntd_lm$residuals, nb2listw(analysis_data_nmds_neighbours))

    Moran I test under randomisation

data:  std_mntd_lm$residuals  
weights: nb2listw(analysis_data_nmds_neighbours)    

Moran I statistic standard deviate = 2.9566, p-value = 0.001555
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.200271681      -0.003257329       0.004738802 
std_mntd_spatial_model = spatial_model(std_mntd_analysis_formula, correlation_function_mntd())
moran.test(std_mntd_spatial_model$residuals, nb2listw(analysis_data_neighbours))

    Moran I test under randomisation

data:  std_mntd_spatial_model$residuals  
weights: nb2listw(analysis_data_neighbours)    

Moran I statistic standard deviate = 4.0485, p-value =
0.00002578
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.290664449      -0.003257329       0.005270844 
moran.test(std_mntd_spatial_model$residuals, nb2listw(analysis_data_nmds_neighbours))

    Moran I test under randomisation

data:  std_mntd_spatial_model$residuals  
weights: nb2listw(analysis_data_nmds_neighbours)    

Moran I statistic standard deviate = 3.1544, p-value =
0.0008041
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.213916976      -0.003257329       0.004740043 
std_mntd_analysis_pred_spatial_plot = plot_spatial_result(std_mntd_spatial_model)
Joining with `by = join_by(explanatory)`
std_mntd_analysis_pred_spatial_plot

Normalised

nrm_mntd_analysis_geo_plot = geom_map_nrm(geom_sf(data = analysis_data, aes(color = mntd_normal, geometry = geometry)), 'MNTD')
nrm_mntd_analysis_geo_plot

nrm_mntd_analysis_data = model_data(analysis_data[!is.na(analysis_data$mntd_normal),], 'mntd_normal')
nrm_mntd_analysis_formula = create_formula('mntd_normal')
nrm_mntd_analysis_result = model_average(nrm_mntd_analysis_formula, nrm_mntd_analysis_data)
Fixed term is "(Intercept)"
nrm_mntd_analysis_result_table = model_summary(nrm_mntd_analysis_result)
nrm_mntd_analysis_result_table
nrm_mntd_analysis_pred_plot = plot_dredge_result(nrm_mntd_analysis_result_table)
nrm_mntd_analysis_pred_plot

Do the residuals still contain spatial autocorrelation from a fitted lm?

nrm_mntd_lm = lm(nrm_mntd_analysis_formula, nrm_mntd_analysis_data)
moran.test(nrm_mntd_lm$residuals, nb2listw(analysis_data_neighbours))

    Moran I test under randomisation

data:  nrm_mntd_lm$residuals  
weights: nb2listw(analysis_data_neighbours)    

Moran I statistic standard deviate = 4.2864, p-value =
0.000009079
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.307465451      -0.003257329       0.005254828 
moran.test(nrm_mntd_lm$residuals, nb2listw(analysis_data_nmds_neighbours))

    Moran I test under randomisation

data:  nrm_mntd_lm$residuals  
weights: nb2listw(analysis_data_nmds_neighbours)    

Moran I statistic standard deviate = 2.0944, p-value = 0.01811
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.140725445      -0.003257329       0.004725908 
nrm_mntd_spatial_model = spatial_model(nrm_mntd_analysis_formula, correlation_function_mntd())
moran.test(nrm_mntd_spatial_model$residuals, nb2listw(analysis_data_neighbours))

    Moran I test under randomisation

data:  nrm_mntd_spatial_model$residuals  
weights: nb2listw(analysis_data_neighbours)    

Moran I statistic standard deviate = 4.7455, p-value =
0.00000104
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.340740843      -0.003257329       0.005254693 
moran.test(nrm_mntd_spatial_model$residuals, nb2listw(analysis_data_nmds_neighbours))

    Moran I test under randomisation

data:  nrm_mntd_spatial_model$residuals  
weights: nb2listw(analysis_data_nmds_neighbours)    

Moran I statistic standard deviate = 2.3578, p-value = 0.009192
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.158827607      -0.003257329       0.004725789 
nrm_mntd_analysis_pred_spatial_plot = plot_spatial_result(nrm_mntd_spatial_model)
Joining with `by = join_by(explanatory)`
nrm_mntd_analysis_pred_spatial_plot

Gape width - FDiv

Standardised

std_gape_fdiv_analysis_geo_plot = geom_map_std(geom_sf(data = analysis_data, aes(color = beak_width_fdiv_standard, geometry = geometry)), 'Beak Width FDiv')
std_gape_fdiv_analysis_geo_plot

std_gape_fdiv_analysis_data = model_data(analysis_data[!is.na(analysis_data$beak_width_fdiv_standard),], 'beak_width_fdiv_standard')
std_gape_fdiv_analysis_formula = create_formula('beak_width_fdiv_standard')
std_gape_fdiv_analysis_result = model_average(std_gape_fdiv_analysis_formula, std_gape_fdiv_analysis_data)
Fixed term is "(Intercept)"
std_gape_fdiv_analysis_result_table = model_summary(std_gape_fdiv_analysis_result)
std_gape_fdiv_analysis_result_table
std_gape_fdiv_analysis_pred_plot = plot_dredge_result(std_gape_fdiv_analysis_result_table)
std_gape_fdiv_analysis_pred_plot

Do the residuals still contain spatial autocorrelation from a fitted lm?

std_gape_fdiv_lm = lm(std_gape_fdiv_analysis_formula, std_gape_fdiv_analysis_data)
moran.test(std_gape_fdiv_lm$residuals, nb2listw(analysis_data_neighbours))

    Moran I test under randomisation

data:  std_gape_fdiv_lm$residuals  
weights: nb2listw(analysis_data_neighbours)    

Moran I statistic standard deviate = 3.5059, p-value =
0.0002275
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.251017001      -0.003257329       0.005260172 
moran.test(std_gape_fdiv_lm$residuals, nb2listw(analysis_data_nmds_neighbours))

    Moran I test under randomisation

data:  std_gape_fdiv_lm$residuals  
weights: nb2listw(analysis_data_nmds_neighbours)    

Moran I statistic standard deviate = 1.6582, p-value = 0.04864
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.110794578      -0.003257329       0.004730625 
std_gape_fdiv_spatial_model = spatial_model(std_gape_fdiv_analysis_formula, correlation_function_fdiv())
moran.test(std_gape_fdiv_spatial_model$residuals, nb2listw(analysis_data_neighbours))

    Moran I test under randomisation

data:  std_gape_fdiv_spatial_model$residuals  
weights: nb2listw(analysis_data_neighbours)    

Moran I statistic standard deviate = 3.5695, p-value =
0.0001789
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.255641928      -0.003257329       0.005260840 
moran.test(std_gape_fdiv_spatial_model$residuals, nb2listw(analysis_data_nmds_neighbours))

    Moran I test under randomisation

data:  std_gape_fdiv_spatial_model$residuals  
weights: nb2listw(analysis_data_nmds_neighbours)    

Moran I statistic standard deviate = 1.868, p-value = 0.03088
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.125233427      -0.003257329       0.004731214 
std_gape_fdiv_analysis_pred_spatial_plot = plot_spatial_result(std_gape_fdiv_spatial_model)
Joining with `by = join_by(explanatory)`
std_gape_fdiv_analysis_pred_spatial_plot

Normalised

nrm_gape_fdiv_analysis_geo_plot = geom_map_nrm(geom_sf(data = analysis_data, aes(color = beak_width_fdiv_normal, geometry = geometry)), 'Beak Width FDiv')
nrm_gape_fdiv_analysis_geo_plot

nrm_gape_fdiv_analysis_data = model_data(analysis_data[!is.na(analysis_data$beak_width_fdiv_normal),], 'beak_width_fdiv_normal')
nrm_gape_fdiv_analysis_formula = create_formula('beak_width_fdiv_normal')
nrm_gape_fdiv_analysis_result = model_average(nrm_gape_fdiv_analysis_formula, nrm_gape_fdiv_analysis_data)
Fixed term is "(Intercept)"
nrm_gape_fdiv_analysis_result_table = model_summary(nrm_gape_fdiv_analysis_result)
nrm_gape_fdiv_analysis_result_table
nrm_gape_fdiv_analysis_pred_plot = plot_dredge_result(nrm_gape_fdiv_analysis_result_table)
nrm_gape_fdiv_analysis_pred_plot

Do the residuals still contain spatial autocorrelation from a fitted lm?

nrm_gape_fdiv_lm = lm(nrm_gape_fdiv_analysis_formula, nrm_gape_fdiv_analysis_data)
moran.test(nrm_gape_fdiv_lm$residuals, nb2listw(analysis_data_neighbours))

    Moran I test under randomisation

data:  nrm_gape_fdiv_lm$residuals  
weights: nb2listw(analysis_data_neighbours)    

Moran I statistic standard deviate = 4.3357, p-value =
0.000007265
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.311050869      -0.003257329       0.005255227 
moran.test(nrm_gape_fdiv_lm$residuals, nb2listw(analysis_data_nmds_neighbours))

    Moran I test under randomisation

data:  nrm_gape_fdiv_lm$residuals  
weights: nb2listw(analysis_data_nmds_neighbours)    

Moran I statistic standard deviate = 1.4217, p-value = 0.07756
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.094482089      -0.003257329       0.004726261 
nrm_gape_fdiv_spatial_model = spatial_model(nrm_gape_fdiv_analysis_formula, correlation_function_fdiv())
moran.test(nrm_gape_fdiv_spatial_model$residuals, nb2listw(analysis_data_neighbours))

    Moran I test under randomisation

data:  nrm_gape_fdiv_spatial_model$residuals  
weights: nb2listw(analysis_data_neighbours)    

Moran I statistic standard deviate = 4.4995, p-value =
0.000003406
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.322936781      -0.003257329       0.005255637 
moran.test(nrm_gape_fdiv_spatial_model$residuals, nb2listw(analysis_data_nmds_neighbours))

    Moran I test under randomisation

data:  nrm_gape_fdiv_spatial_model$residuals  
weights: nb2listw(analysis_data_nmds_neighbours)    

Moran I statistic standard deviate = 1.7347, p-value = 0.0414
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.116002600      -0.003257329       0.004726623 
nrm_gape_fdiv_analysis_pred_spatial_plot = plot_spatial_result(nrm_gape_fdiv_spatial_model)
Joining with `by = join_by(explanatory)`
nrm_gape_fdiv_analysis_pred_spatial_plot

HWI - FDiv

Standardised

std_hwi_fdiv_analysis_geo_plot = geom_map_std(geom_sf(data = analysis_data, aes(color = hwi_fdiv_standard, geometry = geometry)), 'HWI FDiv')
std_hwi_fdiv_analysis_geo_plot

std_hwi_fdiv_analysis_data = model_data(analysis_data[!is.na(analysis_data$hwi_fdiv_standard),], 'hwi_fdiv_standard')
std_hwi_fdiv_analysis_formula = create_formula('hwi_fdiv_standard')
std_hwi_fdiv_analysis_result = model_average(std_hwi_fdiv_analysis_formula, std_hwi_fdiv_analysis_data)
Fixed term is "(Intercept)"
std_hwi_fdiv_analysis_result_table = model_summary(std_hwi_fdiv_analysis_result)
std_hwi_fdiv_analysis_result_table
std_hwi_fdiv_analysis_pred_plot = plot_dredge_result(std_hwi_fdiv_analysis_result_table)
std_hwi_fdiv_analysis_pred_plot

std_hwi_fdiv_lm = lm(std_hwi_fdiv_analysis_formula, std_hwi_fdiv_analysis_data)
moran.test(std_hwi_fdiv_lm$residuals, nb2listw(analysis_data_neighbours))

    Moran I test under randomisation

data:  std_hwi_fdiv_lm$residuals  
weights: nb2listw(analysis_data_neighbours)    

Moran I statistic standard deviate = 6.1738, p-value =
0.0000000003333
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.444286636      -0.003257329       0.005254904 
moran.test(std_hwi_fdiv_lm$residuals, nb2listw(analysis_data_nmds_neighbours))

    Moran I test under randomisation

data:  std_hwi_fdiv_lm$residuals  
weights: nb2listw(analysis_data_nmds_neighbours)    

Moran I statistic standard deviate = 3.8009, p-value =
0.00007207
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.258041658      -0.003257329       0.004725975 
std_hwi_fdiv_spatial_model = spatial_model(std_hwi_fdiv_analysis_formula, correlation_function_fdiv())
moran.test(std_hwi_fdiv_spatial_model$residuals, nb2listw(analysis_data_neighbours))

    Moran I test under randomisation

data:  std_hwi_fdiv_spatial_model$residuals  
weights: nb2listw(analysis_data_neighbours)    

Moran I statistic standard deviate = 7.2179, p-value =
0.0000000000002639
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.520272713      -0.003257329       0.005260863 
moran.test(std_hwi_fdiv_spatial_model$residuals, nb2listw(analysis_data_nmds_neighbours))

    Moran I test under randomisation

data:  std_hwi_fdiv_spatial_model$residuals  
weights: nb2listw(analysis_data_nmds_neighbours)    

Moran I statistic standard deviate = 3.3854, p-value =
0.0003554
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.229604043      -0.003257329       0.004731234 
std_hwi_fdiv_analysis_pred_spatial_plot = plot_spatial_result(std_hwi_fdiv_spatial_model)
Joining with `by = join_by(explanatory)`
std_hwi_fdiv_analysis_pred_spatial_plot

Normalised

nrm_hwi_fdiv_analysis_geo_plot = geom_map_nrm(geom_sf(data = analysis_data, aes(color = hwi_fdiv_normal, geometry = geometry)), 'HWI FDiv')
nrm_hwi_fdiv_analysis_geo_plot

nrm_hwi_fdiv_analysis_data = model_data(analysis_data[!is.na(analysis_data$hwi_fdiv_normal),], 'hwi_fdiv_normal')
nrm_hwi_fdiv_analysis_formula = create_formula('hwi_fdiv_normal')
nrm_hwi_fdiv_analysis_result = model_average(nrm_hwi_fdiv_analysis_formula, nrm_hwi_fdiv_analysis_data)
Fixed term is "(Intercept)"
nrm_hwi_fdiv_analysis_result_table = model_summary(nrm_hwi_fdiv_analysis_result)
nrm_hwi_fdiv_analysis_result_table
nrm_hwi_fdiv_analysis_pred_plot = plot_dredge_result(nrm_hwi_fdiv_analysis_result_table)
nrm_hwi_fdiv_analysis_pred_plot

nrm_hwi_fdiv_lm = lm(nrm_hwi_fdiv_analysis_formula, nrm_hwi_fdiv_analysis_data)
moran.test(nrm_hwi_fdiv_lm$residuals, nb2listw(analysis_data_neighbours))

    Moran I test under randomisation

data:  nrm_hwi_fdiv_lm$residuals  
weights: nb2listw(analysis_data_neighbours)    

Moran I statistic standard deviate = 7.0294, p-value =
0.000000000001037
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.505183959      -0.003257329       0.005231738 
moran.test(nrm_hwi_fdiv_lm$residuals, nb2listw(analysis_data_nmds_neighbours))

    Moran I test under randomisation

data:  nrm_hwi_fdiv_lm$residuals  
weights: nb2listw(analysis_data_nmds_neighbours)    

Moran I statistic standard deviate = 3.6271, p-value =
0.0001433
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.245547978      -0.003257329       0.004705532 
nrm_hwi_fdiv_spatial_model = spatial_model(nrm_hwi_fdiv_analysis_formula, correlation_function_fdiv())
moran.test(nrm_hwi_fdiv_spatial_model$residuals, nb2listw(analysis_data_neighbours))

    Moran I test under randomisation

data:  nrm_hwi_fdiv_spatial_model$residuals  
weights: nb2listw(analysis_data_neighbours)    

Moran I statistic standard deviate = 8.1492, p-value <
0.00000000000000022
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.587846115      -0.003257329       0.005261332 
moran.test(nrm_hwi_fdiv_spatial_model$residuals, nb2listw(analysis_data_nmds_neighbours))

    Moran I test under randomisation

data:  nrm_hwi_fdiv_spatial_model$residuals  
weights: nb2listw(analysis_data_nmds_neighbours)    

Moran I statistic standard deviate = 3.5151, p-value =
0.0002198
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.238537056      -0.003257329       0.004731648 
nrm_hwi_fdiv_analysis_pred_spatial_plot = plot_spatial_result(nrm_hwi_fdiv_spatial_model)
Joining with `by = join_by(explanatory)`
nrm_hwi_fdiv_analysis_pred_spatial_plot

Mass - FDiv

Standardised

std_mass_fdiv_analysis_geo_plot = geom_map_std(geom_sf(data = analysis_data, aes(color = mass_fdiv_standard, geometry = geometry)), 'Mass FDiv')
std_mass_fdiv_analysis_geo_plot

std_mass_fdiv_analysis_data = model_data(analysis_data[!is.na(analysis_data$mass_fdiv_standard),], 'mass_fdiv_standard')
std_mass_fdiv_analysis_formula = create_formula('mass_fdiv_standard')
std_mass_fdiv_analysis_result <- model_average(std_mass_fdiv_analysis_formula, std_mass_fdiv_analysis_data)
Fixed term is "(Intercept)"
std_mass_fdiv_analysis_result_table = model_summary(std_mass_fdiv_analysis_result)
std_mass_fdiv_analysis_result_table
std_mass_fdiv_analysis_pred_plot = plot_dredge_result(std_mass_fdiv_analysis_result_table)
std_mass_fdiv_analysis_pred_plot

std_mass_fdiv_lm = lm(std_mass_fdiv_analysis_formula, std_mass_fdiv_analysis_data)
moran.test(std_mass_fdiv_lm$residuals, nb2listw(analysis_data_neighbours))

    Moran I test under randomisation

data:  std_mass_fdiv_lm$residuals  
weights: nb2listw(analysis_data_neighbours)    

Moran I statistic standard deviate = 7.4467, p-value =
0.00000000000004785
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.536475629      -0.003257329       0.005253273 
moran.test(std_mass_fdiv_lm$residuals, nb2listw(analysis_data_nmds_neighbours))

    Moran I test under randomisation

data:  std_mass_fdiv_lm$residuals  
weights: nb2listw(analysis_data_nmds_neighbours)    

Moran I statistic standard deviate = 4.7957, p-value =
0.0000008107
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.326373879      -0.003257329       0.004724537 
std_mass_fdiv_spatial_model = spatial_model(std_mass_fdiv_analysis_formula, correlation_function_fdiv())
moran.test(std_mass_fdiv_spatial_model$residuals, nb2listw(analysis_data_neighbours))

    Moran I test under randomisation

data:  std_mass_fdiv_spatial_model$residuals  
weights: nb2listw(analysis_data_neighbours)    

Moran I statistic standard deviate = 7.9698, p-value =
0.0000000000000007945
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.574414910      -0.003257329       0.005253707 
moran.test(std_mass_fdiv_spatial_model$residuals, nb2listw(analysis_data_nmds_neighbours))

    Moran I test under randomisation

data:  std_mass_fdiv_spatial_model$residuals  
weights: nb2listw(analysis_data_nmds_neighbours)    

Moran I statistic standard deviate = 5.3014, p-value =
0.00000005745
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.361152294      -0.003257329       0.004724919 
std_mass_fdiv_analysis_pred_spatial_plot = plot_spatial_result(std_mass_fdiv_spatial_model)
Joining with `by = join_by(explanatory)`
std_mass_fdiv_analysis_pred_spatial_plot

Normalised

nrm_mass_fdiv_analysis_geo_plot = geom_map_nrm(geom_sf(data = analysis_data, aes(color = mass_fdiv_normal, geometry = geometry)), 'Mass FDiv')
nrm_mass_fdiv_analysis_geo_plot

nrm_mass_fdiv_analysis_data = model_data(analysis_data[!is.na(analysis_data$mass_fdiv_normal),], 'mass_fdiv_normal')
nrm_mass_fdiv_analysis_formula = create_formula('mass_fdiv_normal')
nrm_mass_fdiv_analysis_result = model_average(nrm_mass_fdiv_analysis_formula, nrm_mass_fdiv_analysis_data)
Fixed term is "(Intercept)"
nrm_mass_fdiv_analysis_result_table = model_summary(nrm_mass_fdiv_analysis_result)
nrm_mass_fdiv_analysis_result_table
nrm_mass_fdiv_analysis_pred_plot = plot_dredge_result(nrm_mass_fdiv_analysis_result_table)
nrm_mass_fdiv_analysis_pred_plot

nrm_mass_fdiv_lm = lm(nrm_mass_fdiv_analysis_formula, nrm_mass_fdiv_analysis_data)
moran.test(nrm_mass_fdiv_lm$residuals, nb2listw(analysis_data_neighbours))

    Moran I test under randomisation

data:  nrm_mass_fdiv_lm$residuals  
weights: nb2listw(analysis_data_neighbours)    

Moran I statistic standard deviate = 6.9903, p-value =
0.000000000001372
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.503538387      -0.003257329       0.005256267 
moran.test(nrm_mass_fdiv_lm$residuals, nb2listw(analysis_data_nmds_neighbours))

    Moran I test under randomisation

data:  nrm_mass_fdiv_lm$residuals  
weights: nb2listw(analysis_data_nmds_neighbours)    

Moran I statistic standard deviate = 2.9195, p-value = 0.001753
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.197472475      -0.003257329       0.004727179 
nrm_mass_fdiv_spatial_model = spatial_model(nrm_mass_fdiv_analysis_formula, correlation_function_fdiv())
moran.test(nrm_mass_fdiv_spatial_model$residuals, nb2listw(analysis_data_neighbours))

    Moran I test under randomisation

data:  nrm_mass_fdiv_spatial_model$residuals  
weights: nb2listw(analysis_data_neighbours)    

Moran I statistic standard deviate = 7.5406, p-value =
0.0000000000000234
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.543587169      -0.003257329       0.005259210 
moran.test(nrm_mass_fdiv_spatial_model$residuals, nb2listw(analysis_data_nmds_neighbours))

    Moran I test under randomisation

data:  nrm_mass_fdiv_spatial_model$residuals  
weights: nb2listw(analysis_data_nmds_neighbours)    

Moran I statistic standard deviate = 3.2302, p-value =
0.0006185
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.218896280      -0.003257329       0.004729776 
nrm_mass_fdiv_analysis_pred_spatial_plot = plot_spatial_result(nrm_mass_fdiv_spatial_model)
Joining with `by = join_by(explanatory)`
nrm_mass_fdiv_analysis_pred_spatial_plot

Create plot of differences in process response

fig = function(trait_name, response_type, geo_plot, predictor_plot) {
  grid.arrange(
    arrangeGrob(grid::textGrob(trait_name, x = 0.02, hjust = 0, gp=gpar(fontface="bold"))),
    arrangeGrob(grid::textGrob(paste('A)', response_type,'response by city'), x = 0.02, hjust = 0, gp=gpar(fontface="bold"))),
    geo_plot,
    arrangeGrob(grid::textGrob(paste('B)', response_type, 'response predictors'), x = 0.02, hjust = 0, gp=gpar(fontface="bold"))),
    predictor_plot + theme(legend.position = 'bottom') + guides(colour = guide_legend(nrow=2, byrow=TRUE)),
    heights = c(0.25, 0.25, 4, 0.25, 3),
    nrow = 5
  )
}


fig('MNTD', 'Standardised', std_mntd_analysis_geo_plot, std_mntd_analysis_pred_plot)

MNTD

jpeg(filename(FIGURES_OUTPUT_DIR, 'figure2_std_mntd.jpg'), width = 200, height = 220, units = 'mm', res = 300)
grid.arrange(
  arrangeGrob(fig('MNTD', 'Standardised', std_mntd_analysis_geo_plot, std_mntd_analysis_pred_plot)),
  ncol = 1
  )
dev.off()
null device 
          1 
jpeg(filename(FIGURES_OUTPUT_DIR, 'figure2_nrm_mntd.jpg'), width = 200, height = 220, units = 'mm', res = 300)
grid.arrange(
  arrangeGrob(fig('MNTD', 'Normalised', nrm_mntd_analysis_geo_plot, nrm_mntd_analysis_pred_plot)),
  ncol = 1
  )
dev.off()
null device 
          1 

Beak Width

jpeg(filename(FIGURES_OUTPUT_DIR, 'figure2_std_beak_width.jpg'), width = 200, height = 220, units = 'mm', res = 300)
grid.arrange(
  arrangeGrob(fig('Beak Width', 'Standardised', std_gape_fdiv_analysis_geo_plot, std_gape_fdiv_analysis_pred_plot)),
  ncol = 1
  )
dev.off()
null device 
          1 
jpeg(filename(FIGURES_OUTPUT_DIR, 'figure2_nrm_beak_width.jpg'), width = 200, height = 220, units = 'mm', res = 300)
grid.arrange(
  arrangeGrob(fig('Beak Width', 'Normalised', nrm_gape_fdiv_analysis_geo_plot, nrm_gape_fdiv_analysis_pred_plot)),
  ncol = 1
  )
dev.off()
null device 
          1 

HWI

jpeg(filename(FIGURES_OUTPUT_DIR, 'figure2_std_hwi.jpg'), width = 200, height = 220, units = 'mm', res = 300)
grid.arrange(
  arrangeGrob(fig('HWI (Hand-wing Index)', 'Standardised', std_hwi_fdiv_analysis_geo_plot, std_hwi_fdiv_analysis_pred_plot)),
  ncol = 1
  )
dev.off()
null device 
          1 
jpeg(filename(FIGURES_OUTPUT_DIR, 'figure2_nrm_hwi.jpg'), width = 200, height = 220, units = 'mm', res = 300)
grid.arrange(
  arrangeGrob(fig('HWI (Hand-wing Index)', 'Normalised', nrm_hwi_fdiv_analysis_geo_plot, nrm_hwi_fdiv_analysis_pred_plot)),
  ncol = 1
  )
dev.off()
null device 
          1 

Mass

jpeg(filename(FIGURES_OUTPUT_DIR, 'figure2_std_mass.jpg'), width = 200, height = 220, units = 'mm', res = 300)
grid.arrange(
  arrangeGrob(fig('Mass', 'Standardised', std_mass_fdiv_analysis_geo_plot, std_mass_fdiv_analysis_pred_plot)),
  ncol = 1
  )
dev.off()
null device 
          1 
jpeg(filename(FIGURES_OUTPUT_DIR, 'figure2_nrm_mass.jpg'), width = 200, height = 220, units = 'mm', res = 300)
grid.arrange(
  arrangeGrob(fig('Mass', 'Normalised', nrm_mass_fdiv_analysis_geo_plot, nrm_mass_fdiv_analysis_pred_plot)),
  ncol = 1
  )
dev.off()
null device 
          1 

Compare to spatial model

plot_grid(
  plot_grid(
    ggdraw() + 
    draw_label(
      "Spatial Model (standardised)",
      fontface = 'bold',
      x = 0,
      hjust = 0
    ),
    ggdraw() + 
    draw_label(
      "Spatial Model (normalised)",
      fontface = 'bold',
      x = 0,
      hjust = 0
    ),
    ggdraw() + 
    draw_label(
      "Dredge Result (standardised)",
      fontface = 'bold',
      x = 0,
      hjust = 0
    ),
    ggdraw() + 
    draw_label(
      "Dredge Result (normalised)",
      fontface = 'bold',
      x = 0,
      hjust = 0
    ),
    nrow = 1
  ),
  plot_grid(
    std_mntd_analysis_pred_spatial_plot + theme(legend.position="none") + scale_x_continuous(name = '', limits = c(-3, 3)) + ylab(''), 
    nrm_mntd_analysis_pred_spatial_plot + theme(legend.position="none") + scale_x_continuous(name = '', limits = c(-1, 1)) + ylab(''), 
    std_mntd_analysis_pred_plot + theme(legend.position="none") + scale_x_continuous(name = '', limits = c(-3, 3)) + ylab(''), 
    nrm_mntd_analysis_pred_plot + theme(legend.position="none") + scale_x_continuous(name = '', limits = c(-1, 1)) + ylab(''), 
    nrow = 1
  ) + draw_label("MNTD", size = 16, angle = 90, x = 0.01, y = 0.5),
  plot_grid(
    std_gape_fdiv_analysis_pred_spatial_plot + theme(legend.position="none") + scale_x_continuous(name = '', limits = c(-3, 3)) + ylab(''), 
    nrm_gape_fdiv_analysis_pred_spatial_plot + theme(legend.position="none") + scale_x_continuous(name = '', limits = c(-1, 1)) + ylab(''), 
    std_gape_fdiv_analysis_pred_plot + theme(legend.position="none") + scale_x_continuous(name = '', limits = c(-3, 3)) + ylab(''),  
    nrm_gape_fdiv_analysis_pred_plot + theme(legend.position="none") + scale_x_continuous(name = '', limits = c(-1, 1)) + ylab(''), 
    nrow = 1
  ) + draw_label("Beak Width", size = 16, angle = 90, x = 0.01, y = 0.5),
  plot_grid(
    std_hwi_fdiv_analysis_pred_spatial_plot + theme(legend.position="none") + scale_x_continuous(name = '', limits = c(-3, 3)) + ylab(''), 
    nrm_hwi_fdiv_analysis_pred_spatial_plot + theme(legend.position="none") + scale_x_continuous(name = '', limits = c(-1, 1)) + ylab(''), 
    std_hwi_fdiv_analysis_pred_plot + theme(legend.position="none") + scale_x_continuous(name = '', limits = c(-3, 3)) + ylab(''),  
    nrm_hwi_fdiv_analysis_pred_plot + theme(legend.position="none") + scale_x_continuous(name = '', limits = c(-1, 1)) + ylab(''), 
    nrow = 1
  ) + draw_label("HWI", size = 16, angle = 90, x = 0.01, y = 0.5),
  plot_grid(
    std_mass_fdiv_analysis_pred_spatial_plot + theme(legend.position="none") + scale_x_continuous(name = '', limits = c(-3, 3)) + ylab(''), 
    nrm_mass_fdiv_analysis_pred_spatial_plot + theme(legend.position="none") + scale_x_continuous(name = '', limits = c(-1, 1)) + ylab(''), 
    std_mass_fdiv_analysis_pred_plot + theme(legend.position="none") + scale_x_continuous(name = '', limits = c(-3, 3)) + ylab(''),  
    nrm_mass_fdiv_analysis_pred_plot + theme(legend.position="none") + scale_x_continuous(name = '', limits = c(-1, 1)) + ylab(''), 
    nrow = 1
  ) + draw_label("Mass", size = 16, angle = 90, x = 0.01, y = 0.5), 
  nrow = 5, rel_heights = c(1, 4, 4, 4, 4)
)
`geom_line()`: Each group consists of only one observation.
ℹ Do you need to adjust the group aesthetic?`geom_line()`: Each group consists of only one observation.
ℹ Do you need to adjust the group aesthetic?`geom_line()`: Each group consists of only one observation.
ℹ Do you need to adjust the group aesthetic?`geom_line()`: Each group consists of only one observation.
ℹ Do you need to adjust the group aesthetic?`geom_line()`: Each group consists of only one observation.
ℹ Do you need to adjust the group aesthetic?`geom_line()`: Each group consists of only one observation.
ℹ Do you need to adjust the group aesthetic?`geom_line()`: Each group consists of only one observation.
ℹ Do you need to adjust the group aesthetic?`geom_line()`: Each group consists of only one observation.
ℹ Do you need to adjust the group aesthetic?`geom_line()`: Each group consists of only one observation.
ℹ Do you need to adjust the group aesthetic?`geom_line()`: Each group consists of only one observation.
ℹ Do you need to adjust the group aesthetic?`geom_line()`: Each group consists of only one observation.
ℹ Do you need to adjust the group aesthetic?`geom_line()`: Each group consists of only one observation.
ℹ Do you need to adjust the group aesthetic?`geom_line()`: Each group consists of only one observation.
ℹ Do you need to adjust the group aesthetic?`geom_line()`: Each group consists of only one observation.
ℹ Do you need to adjust the group aesthetic?`geom_line()`: Each group consists of only one observation.
ℹ Do you need to adjust the group aesthetic?`geom_line()`: Each group consists of only one observation.
ℹ Do you need to adjust the group aesthetic?
ggsave(filename(FIGURES_OUTPUT_DIR, 'process_response_vs_spatial.jpg'), width = 4000, height = 3200, units = 'px')

Compare metrics against each other

ggplot(analysis_data, aes(x = beak_width_fdiv_standard, y = mntd_standard, colour = core_realm)) + 
  geom_point() +
  ylab("MNTD") + 
  xlab("Beak Width FDiv") +
  theme_bw() + labs(color = "Realm")

ggplot(analysis_data, aes(x = hwi_fdiv_standard, y = mntd_standard, colour = core_realm)) + 
  geom_point() +
  ylab("MNTD") + 
  xlab("HWI FDiv") +
  theme_bw() + labs(color = "Realm")

ggplot(analysis_data, aes(x = hwi_fdiv_standard, y = beak_width_fdiv_standard, colour = core_realm)) + 
  geom_point() +
  ylab("Beak Width FDiv") + 
  xlab("HWI FDiv") +
  theme_bw() + labs(color = "Realm")

mntd_fdiv_analysis = analysis_data %>% 
  dplyr::select(city_id,  mntd_standard, hwi_fdiv_standard, beak_width_fdiv_standard, mass_fdiv_standard) %>%
  left_join(community_summary) %>%
  mutate(urban_pool_perc = urban_pool_size * 100 / regional_pool_size)
Joining with `by = join_by(city_id)`
mntd_fdiv_analysis
ggpairs(mntd_fdiv_analysis %>% dplyr::select(mntd_standard, hwi_fdiv_standard, beak_width_fdiv_standard, mass_fdiv_standard, regional_pool_size, urban_pool_size, urban_pool_perc), columnLabels = c('MNTD', 'HWI FD', 'Bk FD', 'Mss FD', 'Region Rich.', 'Urban Rich.', '% Urban'))
ggsave(filename(FIGURES_OUTPUT_DIR, 'appendix_standarised_correlation.jpg'))
Saving 5.68 x 3.51 in image

Intraction term between introduced species and realm

create_interaction_formula = function(response_var) {
  as.formula(paste(response_var, '~ core_realm * has_introduced_species'))
}
interaction_type_labels = function(p) {
  explanatory_levels = all_explanatories[all_explanatories %in% p$explanatory]
  p$explanatory <- factor(p$explanatory, levels = explanatory_levels)
  
  p$type <- 'Interaction'
  p$type[p$explanatory %in% c('core_realmNearctic', 'core_realmNeotropic', 'core_realmAfrotropic', 'core_realmIndomalayan', 'core_realmAustralasia')] <- 'Realm'
  p$type[p$explanatory %in% c('has_introduced_speciesNo introduced species', 'has_introduced_speciesIntroduced species')] <- 'Introduced species'
  p
}
plot_dredge_interaction_result = function(result_table, mu = 0) {
  p = result_table[result_table$model == 'full',]
  p = interaction_type_labels(p)

  ggplot(p, aes(y = explanatory, x = estimate, colour = type)) + 
    geom_line() +
    geom_point() +
    geom_errorbar(aes(xmin=estimate-error, xmax=estimate+error), width=.2,
                   position=position_dodge(0.05)) +
    scale_y_discrete(
      limits = rev(levels(p$explanatory)), 
      labels = explanatory_labels) +
    scale_colour_manual(
      values = c(realm_colour, interaction_colour, introduced_species_colour), 
      breaks = c('Realm', 'Interaction', 'Introduced species')) +
    theme_bw() +
    geom_vline(xintercept=mu, linetype="dotted") +
    guides(colour=guide_legend(title="Predictor type")) + xlab('Difference in response from 0\nhabitat filtering (< 0) and competitive interactions (> 0)\n± Standard Error') + ylab('Predictor') +
    theme(legend.justification = "top")
}

MNTD

Standardised

std_mntd_interaction_formula = create_interaction_formula('mntd_standard')
std_mntd_interaction_result = model_average(std_mntd_interaction_formula, std_mntd_analysis_data)
Fixed term is "(Intercept)"
std_mntd_interaction_result_table = model_summary(std_mntd_interaction_result)
std_mntd_interaction_result_table
std_mntd_interaction_pred_plot = plot_dredge_interaction_result(std_mntd_interaction_result_table)
std_mntd_interaction_pred_plot
ggsave(filename(FIGURES_OUTPUT_DIR, 'figure4_std_mntd_interaction.jpg'))
Saving 5.68 x 3.51 in image

Normalised

nrm_mntd_interaction_formula = create_interaction_formula('mntd_normal')
nrm_mntd_interaction_result = model_average(nrm_mntd_interaction_formula, nrm_mntd_analysis_data)
Fixed term is "(Intercept)"
nrm_mntd_interaction_result_table = model_summary(nrm_mntd_interaction_result)
nrm_mntd_interaction_result_table
nrm_mntd_interaction_pred_plot = plot_dredge_interaction_result(nrm_mntd_interaction_result_table)
nrm_mntd_interaction_pred_plot
ggsave(filename(FIGURES_OUTPUT_DIR, 'figure4_nrm_mntd_interaction.jpg'))
Saving 5.68 x 3.51 in image

Beak Width

Standardised

std_gape_interaction_formula = create_interaction_formula('beak_width_fdiv_standard')
std_gape_interaction_result = model_average(std_gape_interaction_formula, std_gape_fdiv_analysis_data)
Fixed term is "(Intercept)"
std_gape_interaction_result_table = model_summary(std_gape_interaction_result)
std_mntd_interaction_result_table
std_gape_interaction_pred_plot = plot_dredge_interaction_result(std_gape_interaction_result_table)
std_gape_interaction_pred_plot
ggsave(filename(FIGURES_OUTPUT_DIR, 'figure4_std_beak_width_interaction.jpg'))
Saving 5.68 x 3.51 in image

Normalised

nrm_gape_interaction_formula = create_interaction_formula('beak_width_fdiv_normal')
nrm_gape_interaction_result = model_average(nrm_gape_interaction_formula, nrm_gape_fdiv_analysis_data)
Fixed term is "(Intercept)"
nrm_gape_interaction_result_table = model_summary(nrm_gape_interaction_result)
nrm_gape_interaction_result_table
nrm_gape_interaction_pred_plot = plot_dredge_interaction_result(nrm_gape_interaction_result_table)
nrm_gape_interaction_pred_plot
ggsave(filename(FIGURES_OUTPUT_DIR, 'figure4_nrm_beak_width_interaction.jpg'))
Saving 5.68 x 3.51 in image

HWI

Standardised

std_hwi_interaction_formula = create_interaction_formula('hwi_fdiv_standard')
std_hwi_interaction_result = model_average(std_hwi_interaction_formula, std_hwi_fdiv_analysis_data)
Fixed term is "(Intercept)"
std_hwi_interaction_result_table = model_summary(std_hwi_interaction_result)
std_hwi_interaction_result_table
std_hwi_interaction_pred_plot = plot_dredge_interaction_result(std_hwi_interaction_result_table)
std_hwi_interaction_pred_plot
ggsave(filename(FIGURES_OUTPUT_DIR, 'figure4_std_hwi_interaction.jpg'))
Saving 5.68 x 3.51 in image

Normalised

nrm_hwi_interaction_formula = create_interaction_formula('hwi_fdiv_normal')
nrm_hwi_interaction_result = model_average(nrm_hwi_interaction_formula, nrm_hwi_fdiv_analysis_data)
Fixed term is "(Intercept)"
nrm_hwi_interaction_result_table = model_summary(nrm_hwi_interaction_result)
nrm_hwi_interaction_result_table
nrm_hwi_interaction_pred_plot = plot_dredge_interaction_result(nrm_hwi_interaction_result_table)
nrm_hwi_interaction_pred_plot
ggsave(filename(FIGURES_OUTPUT_DIR, 'figure4_nrm_hwi_interaction.jpg'))
Saving 5.68 x 3.51 in image

Mass

Standardised

std_mass_interaction_formula = create_interaction_formula('mass_fdiv_standard')
std_mass_interaction_result = model_average(std_mass_interaction_formula, std_mass_fdiv_analysis_data)
Fixed term is "(Intercept)"
std_mass_interaction_result_table = model_summary(std_mass_interaction_result)
std_mass_interaction_result_table
std_mass_interaction_pred_plot = plot_dredge_interaction_result(std_mass_interaction_result_table)
std_mass_interaction_pred_plot
ggsave(filename(FIGURES_OUTPUT_DIR, 'figure4_std_mass_interaction.jpg'))
Saving 5.68 x 3.51 in image

Normalised

nrm_mass_interaction_formula = create_interaction_formula('mass_fdiv_normal')
nrm_mass_interaction_result = model_average(nrm_mass_interaction_formula, nrm_mass_fdiv_analysis_data)
Fixed term is "(Intercept)"
nrm_mass_interaction_result_table = model_summary(nrm_mass_interaction_result)
nrm_mass_interaction_result_table
nrm_mass_interaction_pred_plot = plot_dredge_interaction_result(nrm_mass_interaction_result_table)
nrm_mass_interaction_pred_plot
ggsave(filename(FIGURES_OUTPUT_DIR, 'figure4_nrm_mass_interaction.jpg'))
Saving 5.68 x 3.51 in image

LS0tCnRpdGxlOiAiTWV0cmljcyBmb3IgYXNzZXNzaW5nIGNvbW11bml0eSBhc3NlbWJseSBwcm9jZXNzZXMiCm91dHB1dDogaHRtbF9ub3RlYm9vawpiaWJsaW9ncmFwaHk6IC4uL3JlZi5iaWIgIAotLS0KCmBgYHtyfQpzb3VyY2UoJy4uL2Vudi5SJykKYGBgCgpgYGB7cn0KY29tbXVuaXR5X2RhdGEgPSByZWFkX2NzdihmaWxlbmFtZShDT01NVU5JVFlfT1VUUFVUX0RJUiwgJ2NvbW11bml0eV9hc3NlbWJseV9tZXRyaWNzX3VzaW5nX3JlbGF0aXZlX2FidW5kYW5jZS5jc3YnKSkKaGVhZChjb21tdW5pdHlfZGF0YSkKY29sbmFtZXMoY29tbXVuaXR5X2RhdGEpCmBgYAoKYGBge3J9Cm1pbihjb21tdW5pdHlfZGF0YSRtbnRkX3N0YW5kYXJkKQptYXgoY29tbXVuaXR5X2RhdGEkbW50ZF9zdGFuZGFyZCkKbWluKGNvbW11bml0eV9kYXRhJGJlYWtfd2lkdGhfZmRpdl9zdGFuZGFyZCkKbWF4KGNvbW11bml0eV9kYXRhJGJlYWtfd2lkdGhfZmRpdl9zdGFuZGFyZCkKbWluKGNvbW11bml0eV9kYXRhJGh3aV9mZGl2X3N0YW5kYXJkKQptYXgoY29tbXVuaXR5X2RhdGEkaHdpX2ZkaXZfc3RhbmRhcmQpCm1pbihjb21tdW5pdHlfZGF0YSRtYXNzX2ZkaXZfc3RhbmRhcmQpCm1heChjb21tdW5pdHlfZGF0YSRtYXNzX2ZkaXZfc3RhbmRhcmQpCmBgYAoKYGBge3J9Cm1pbihjb21tdW5pdHlfZGF0YSRtbnRkX25vcm1hbCkKbWF4KGNvbW11bml0eV9kYXRhJG1udGRfbm9ybWFsKQptaW4oY29tbXVuaXR5X2RhdGEkYmVha193aWR0aF9mZGl2X25vcm1hbCkKbWF4KGNvbW11bml0eV9kYXRhJGJlYWtfd2lkdGhfZmRpdl9ub3JtYWwpCm1pbihjb21tdW5pdHlfZGF0YSRod2lfZmRpdl9ub3JtYWwpCm1heChjb21tdW5pdHlfZGF0YSRod2lfZmRpdl9ub3JtYWwpCm1pbihjb21tdW5pdHlfZGF0YSRtYXNzX2ZkaXZfbm9ybWFsKQptYXgoY29tbXVuaXR5X2RhdGEkbWFzc19mZGl2X25vcm1hbCkKYGBgCgpKb2luIG9uIHJlYWxtcwpgYGB7cn0KY2l0eV90b19yZWFsbSA9IHJlYWRfY3N2KGZpbGVuYW1lKENJVFlfREFUQV9PVVRQVVRfRElSLCAncmVhbG1zLmNzdicpKQpjb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtID0gbGVmdF9qb2luKGNvbW11bml0eV9kYXRhLCBjaXR5X3RvX3JlYWxtKQpgYGAKCkNpdGllcyBhcyBwb2ludHMKYGBge3J9CmNpdHlfcG9pbnRzID0gc3RfY2VudHJvaWQocmVhZF9zZihmaWxlbmFtZShDSVRZX0RBVEFfT1VUUFVUX0RJUiwgJ2NpdHlfc2VsZWN0aW9uLnNocCcpKSkgJT4lIGxlZnRfam9pbihjb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtKQpjaXR5X3BvaW50c19jb29yZHMgPSBzdF9jb29yZGluYXRlcyhjaXR5X3BvaW50cykKY2l0eV9wb2ludHMkbGF0aXR1ZGUgPSBjaXR5X3BvaW50c19jb29yZHNbLDFdCmNpdHlfcG9pbnRzJGxvbmdpdHVkZSA9IGNpdHlfcG9pbnRzX2Nvb3Jkc1ssMl0KYGBgCiAgCmBgYHtyfQp3b3JsZF9tYXAgPSByZWFkX2NvdW50cnlfYm91bmRhcmllcygpCmBgYAoKTG9hZCBjb21tdW5pdHkgZGF0YSwgYW5kIGNyZWF0ZSBsb25nIGZvcm1hdCB2ZXJzaW9uCmBgYHtyfQpjb21tdW5pdGllcyA9IHJlYWRfY3N2KGZpbGVuYW1lKENPTU1VTklUWV9PVVRQVVRfRElSLCAnY29tbXVuaXRpZXNfZm9yX2FuYWx5c2lzLmNzdicpKQpjb21tdW5pdGllcwpgYGAKCmBgYHtyfQpjb21tdW5pdHlfc3VtbWFyeSA9IGNvbW11bml0aWVzICU+JSBncm91cF9ieShjaXR5X2lkKSAlPiUgc3VtbWFyaXNlKHJlZ2lvbmFsX3Bvb2xfc2l6ZSA9IG4oKSwgdXJiYW5fcG9vbF9zaXplID0gc3VtKHJlbGF0aXZlX2FidW5kYW5jZV9wcm94eSA+IDApKQpjb21tdW5pdHlfc3VtbWFyeQpgYGAKCkxvYWQgdHJhaXQgZGF0YQpgYGB7cn0KdHJhaXRzID0gcmVhZF9jc3YoZmlsZW5hbWUoVEFYT05PTVlfT1VUUFVUX0RJUiwgJ3RyYWl0c19lYmlyZC5jc3YnKSkKaGVhZCh0cmFpdHMpCmBgYAoKTG9hZCBzcGF0aWFsIHZhcgpgYGB7cn0Kc3BhdGlhbF92YXIgPSByZWFkX2NzdihmaWxlbmFtZShDT01NVU5JVFlfT1VUUFVUX0RJUiwgJ3NwYXRpYWxfdmFyLmNzdicpKSAlPiUgZmlsdGVyKGNpdHlfaWQgJWluJSBjb21tdW5pdHlfc3VtbWFyeSRjaXR5X2lkKQpzcGF0aWFsX3ZhcgpgYGAKCiMgU3VtbWFyeSBtZXRyaWNzIGJ5IFJlYWxtCmBgYHtyfQp0ZXN0X3JlcXVpcmVkX3ZhbHVlcyA9IGZ1bmN0aW9uKG5hbWUsIGRmKSB7CiAgY2F0KHBhc3RlKAogICAgdGVzdF92YWx1ZV93aWxjb3gocGFzdGUobmFtZSwgJ1N0ZDogTU5URCcpLCBkZiRtbnRkX3N0YW5kYXJkKSwKICAgIHRlc3RfdmFsdWVfd2lsY294KHBhc3RlKG5hbWUsICdTdGQ6IEJlYWsgV2lkdGggRkRpdicpLCBkZiRiZWFrX3dpZHRoX2ZkaXZfc3RhbmRhcmQpLAogICAgdGVzdF92YWx1ZV93aWxjb3gocGFzdGUobmFtZSwgJ1N0ZDogSFdJIEZEaXYnKSwgZGYkaHdpX2ZkaXZfc3RhbmRhcmQpLAogICAgdGVzdF92YWx1ZV93aWxjb3gocGFzdGUobmFtZSwgJ1N0ZDogTWFzcyBGRGl2JyksIGRmJG1hc3NfZmRpdl9zdGFuZGFyZCksCiAgICAKICAgIHRlc3RfdmFsdWVfd2lsY294KHBhc3RlKG5hbWUsICdOb3JtOiBNTlREJyksIGRmJG1udGRfbm9ybWFsLCBtdSA9IDAuNSksCiAgICB0ZXN0X3ZhbHVlX3dpbGNveChwYXN0ZShuYW1lLCAnTm9ybTogQmVhayBXaWR0aCBGRGl2JyksIGRmJGJlYWtfd2lkdGhfZmRpdl9ub3JtYWwsIG11ID0gMC41KSwKICAgIHRlc3RfdmFsdWVfd2lsY294KHBhc3RlKG5hbWUsICdOb3JtOiBIV0kgRkRpdicpLCBkZiRod2lfZmRpdl9ub3JtYWwsIG11ID0gMC41KSwKICAgIHRlc3RfdmFsdWVfd2lsY294KHBhc3RlKG5hbWUsICdOb3JtOiBNYXNzIEZEaXYnKSwgZGYkbWFzc19mZGl2X25vcm1hbCwgbXUgPSAwLjUpLAogICAgcGFzdGUoJ04nLCBucm93KGRmKSksCiAgICBzZXAgPSAiXG4iKSkKfQpgYGAKCmBgYHtyfQp0ZXN0X3JlcXVpcmVkX3ZhbHVlcygnR2xvYmFsJywgY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbSkKYGBgCgpgYGB7cn0KdW5pcXVlKGNvbW11bml0eV9kYXRhX3dpdGhfcmVhbG0kY29yZV9yZWFsbSkKYGBgCgpgYGB7cn0KdGVzdF9yZXF1aXJlZF92YWx1ZXMoJ05lYXJjdGljJywgY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbVtjb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtJGNvcmVfcmVhbG0gPT0gJ05lYXJjdGljJyxdKQpgYGAKCmBgYHtyfQp0ZXN0X3JlcXVpcmVkX3ZhbHVlcygnTmVvdHJvcGljJywgY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbVtjb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtJGNvcmVfcmVhbG0gPT0gJ05lb3Ryb3BpYycsXSkKYGBgCgpgYGB7cn0KdGVzdF9yZXF1aXJlZF92YWx1ZXMoJ1BhbGVhcmN0aWMnLCBjb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtW2NvbW11bml0eV9kYXRhX3dpdGhfcmVhbG0kY29yZV9yZWFsbSA9PSAnUGFsZWFyY3RpYycsXSkKYGBgCgpgYGB7cn0KdGVzdF9yZXF1aXJlZF92YWx1ZXMoJ0Fmcm90cm9waWMnLCBjb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtW2NvbW11bml0eV9kYXRhX3dpdGhfcmVhbG0kY29yZV9yZWFsbSA9PSAnQWZyb3Ryb3BpYycsXSkKYGBgCgpgYGB7cn0KdGVzdF9yZXF1aXJlZF92YWx1ZXMoJ0luZG9tYWxheWFuJywgY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbVtjb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtJGNvcmVfcmVhbG0gPT0gJ0luZG9tYWxheWFuJyxdKQpgYGAKCmBgYHtyfQp0ZXN0X3JlcXVpcmVkX3ZhbHVlcygnQXVzdHJhbGFzaWEnLCBjb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtW2NvbW11bml0eV9kYXRhX3dpdGhfcmVhbG0kY29yZV9yZWFsbSA9PSAnQXVzdHJhbGFzaWEnLF0pCmBgYAoKIyMgSG93IHNpZ25pZmljYW50IGFyZSB0aG9zZSBkaWZmZXJlbmNlczoKYGBge3J9CnByaW50KCdTdGFuZGFyZCcpCmtydXNrYWwudGVzdChtbnRkX3N0YW5kYXJkfmNvcmVfcmVhbG0sIGRhdGEgPSBjb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtKQpwYWlyd2lzZS53aWxjb3gudGVzdChjb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtJG1udGRfc3RhbmRhcmQsIGNvbW11bml0eV9kYXRhX3dpdGhfcmVhbG0kY29yZV9yZWFsbSkKcHJpbnQoJ05vcm1hbCcpCmtydXNrYWwudGVzdChtbnRkX25vcm1hbH5jb3JlX3JlYWxtLCBkYXRhID0gY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbSkKcGFpcndpc2Uud2lsY294LnRlc3QoY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbSRtbnRkX25vcm1hbCwgY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbSRjb3JlX3JlYWxtKQpgYGAKCmBgYHtyfQpwcmludCgnU3RhbmRhcmQnKQprcnVza2FsLnRlc3QoYmVha193aWR0aF9mZGl2X3N0YW5kYXJkfmNvcmVfcmVhbG0sIGRhdGEgPSBjb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtKQpwYWlyd2lzZS53aWxjb3gudGVzdChjb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtJGJlYWtfd2lkdGhfZmRpdl9zdGFuZGFyZCwgY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbSRjb3JlX3JlYWxtKQpwcmludCgnTm9ybWFsJykKa3J1c2thbC50ZXN0KGJlYWtfd2lkdGhfZmRpdl9zdGFuZGFyZH5jb3JlX3JlYWxtLCBkYXRhID0gY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbSkKcGFpcndpc2Uud2lsY294LnRlc3QoY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbSRiZWFrX3dpZHRoX2ZkaXZfc3RhbmRhcmQsIGNvbW11bml0eV9kYXRhX3dpdGhfcmVhbG0kY29yZV9yZWFsbSkKYGBgCgpgYGB7cn0KcHJpbnQoJ1N0YW5kYXJkJykKa3J1c2thbC50ZXN0KGh3aV9mZGl2X3N0YW5kYXJkfmNvcmVfcmVhbG0sIGRhdGEgPSBjb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtKQpwYWlyd2lzZS53aWxjb3gudGVzdChjb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtJGh3aV9mZGl2X3N0YW5kYXJkLCBjb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtJGNvcmVfcmVhbG0pCnByaW50KCdOb3JtYWwnKQprcnVza2FsLnRlc3QoaHdpX2ZkaXZfbm9ybWFsfmNvcmVfcmVhbG0sIGRhdGEgPSBjb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtKQpwYWlyd2lzZS53aWxjb3gudGVzdChjb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtJGh3aV9mZGl2X25vcm1hbCwgY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbSRjb3JlX3JlYWxtKQpgYGAKCmBgYHtyfQpwcmludCgnU3RhbmRhcmQnKQprcnVza2FsLnRlc3QobWFzc19mZGl2X3N0YW5kYXJkfmNvcmVfcmVhbG0sIGRhdGEgPSBjb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtKQpwYWlyd2lzZS53aWxjb3gudGVzdChjb21tdW5pdHlfZGF0YV93aXRoX3JlYWxtJG1hc3NfZmRpdl9zdGFuZGFyZCwgY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbSRjb3JlX3JlYWxtKQpwcmludCgnTm9ybWFsJykKa3J1c2thbC50ZXN0KG1hc3NfZmRpdl9ub3JtYWx+Y29yZV9yZWFsbSwgZGF0YSA9IGNvbW11bml0eV9kYXRhX3dpdGhfcmVhbG0pCnBhaXJ3aXNlLndpbGNveC50ZXN0KGNvbW11bml0eV9kYXRhX3dpdGhfcmVhbG0kbWFzc19mZGl2X25vcm1hbCwgY29tbXVuaXR5X2RhdGFfd2l0aF9yZWFsbSRjb3JlX3JlYWxtKQpgYGAKCiMgV2hhdCBmYW1pbGllcyBleGlzdCBpbiB3aGljaCByZWFsbXM/CmBgYHtyfQpjb21tdW5pdGllcyAlPiUgCiAgbGVmdF9qb2luKGNpdHlfdG9fcmVhbG0pICU+JSAKICBtdXRhdGUoZmFtaWx5ID0gZ3N1YiggIiAuKiQiLCAiIiwgZWJpcmRfc3BlY2llc19uYW1lKSkgJT4lCiAgZHBseXI6OnNlbGVjdChmYW1pbHksIGNvcmVfcmVhbG0pICU+JQogIGRpc3RpbmN0KCkgJT4lCiAgYXJyYW5nZShjb3JlX3JlYWxtKQpgYGAKCiMjIFRvdGFsIHVyYmFuIGZhbWlsaWVzCmBgYHtyfQpjb21tdW5pdGllcyAlPiUgCiAgbXV0YXRlKGZhbWlseSA9IGdzdWIoICIgLiokIiwgIiIsIGViaXJkX3NwZWNpZXNfbmFtZSkpICU+JQogIGRwbHlyOjpzZWxlY3QoZmFtaWx5KSAlPiUKICBkaXN0aW5jdCgpICU+JQogIGFycmFuZ2UoKQpgYGAKCm9mIHdoaWNoIHVyYmFuCmBgYHtyfQpjb21tdW5pdGllcyAlPiUgCiAgZmlsdGVyKHJlbGF0aXZlX2FidW5kYW5jZV9wcm94eSA+IDApICU+JQogIG11dGF0ZShmYW1pbHkgPSBnc3ViKCAiIC4qJCIsICIiLCBlYmlyZF9zcGVjaWVzX25hbWUpKSAlPiUKICBkcGx5cjo6c2VsZWN0KGZhbWlseSkgJT4lCiAgZGlzdGluY3QoKSAlPiUKICBhcnJhbmdlKCkKYGBgCgojIFN1bW1hcnkgbWV0cmljcyBieSBpbnRyb2R1Y2VkIHNwZWNpZXMKYGBge3J9CmNvbW11bml0aWVzID0gcmVhZF9jc3YoZmlsZW5hbWUoQ09NTVVOSVRZX09VVFBVVF9ESVIsICdjb21tdW5pdGllc19mb3JfYW5hbHlzaXMuY3N2JykpCmNpdHlfaW50cm9kdWNlZF9zcGVjaWVzID0gY29tbXVuaXRpZXMgJT4lIGdyb3VwX2J5KGNpdHlfaWQpICU+JSBzdW1tYXJpc2UobnVtYmVyX29mX3NwZWNpZXMgPSBuKCkpICU+JSBsZWZ0X2pvaW4oCiAgY29tbXVuaXRpZXMgJT4lIGdyb3VwX2J5KGNpdHlfaWQpICU+JSBmaWx0ZXIob3JpZ2luID09ICdJbnRyb2R1Y2VkJykgJT4lIHN1bW1hcmlzZShudW1iZXJfb2ZfaW50cm9kdWNlZF9zcGVjaWVzID0gbigpKQopICU+JSByZXBsYWNlX25hKGxpc3QobnVtYmVyX29mX2ludHJvZHVjZWRfc3BlY2llcyA9IDApKQoKY29tbXVuaXR5X2RhdGFfd2l0aF9pbnRyb2R1Y3Rpb25zID0gbGVmdF9qb2luKGNvbW11bml0eV9kYXRhLCBjaXR5X2ludHJvZHVjZWRfc3BlY2llcykKY29tbXVuaXR5X2RhdGFfd2l0aF9pbnRyb2R1Y3Rpb25zJGhhc19pbnRyb2R1Y2VkX3NwZWNpZXMgPSBjb21tdW5pdHlfZGF0YV93aXRoX2ludHJvZHVjdGlvbnMkbnVtYmVyX29mX2ludHJvZHVjZWRfc3BlY2llcyA+IDAKY29tbXVuaXR5X2RhdGFfd2l0aF9pbnRyb2R1Y3Rpb25zCmBgYAoKYGBge3J9CmNvbW11bml0aWVzICU+JSAKICBmaWx0ZXIob3JpZ2luID09ICdJbnRyb2R1Y2VkJykgJT4lCiAgZHBseXI6OnNlbGVjdChlYmlyZF9zcGVjaWVzX25hbWUpICU+JQogIGdyb3VwX2J5KGViaXJkX3NwZWNpZXNfbmFtZSkgJT4lCiAgc3VtbWFyaXNlKHRvdGFsX2NpdGllcyA9IG4oKSkgJT4lCiAgYXJyYW5nZShkZXNjKHRvdGFsX2NpdGllcykpCmBgYAoKYGBge3J9CmNvbW11bml0eV9kYXRhX3dpdGhfaW50cm9kdWN0aW9uc1ssYygnbW50ZF9zdGFuZGFyZCcsICdoYXNfaW50cm9kdWNlZF9zcGVjaWVzJyldCmBgYAoKYGBge3J9CmNvbW11bml0eV9kYXRhX3dpdGhfaW50cm9kdWN0aW9ucyAlPiUgZ3JvdXBfYnkoaGFzX2ludHJvZHVjZWRfc3BlY2llcykgJT4lIHN1bW1hcmlzZSgKICB0b3RhbF9jaXRpZXMgPSBuKCksIAogIAogIG1lYW5fbW50ZF9zdGQgPSBtZWFuKG1udGRfc3RhbmRhcmQsIG5hLnJtID0gVCksCiAgbWVkaWFuX21udGRfc3RkID0gbWVkaWFuKG1udGRfc3RhbmRhcmQsIG5hLnJtID0gVCksCiAgc2RfbW50ZF9zdGQgPSBzZChtbnRkX3N0YW5kYXJkLCBuYS5ybSA9IFQpLAogIAogIG1lYW5fbWFzc19mZGl2X3N0ZCA9IG1lYW4obWFzc19mZGl2X3N0YW5kYXJkLCBuYS5ybSA9IFQpLAogIG1lZGlhbl9tYXNzX2ZkaXZfc3RkID0gbWVkaWFuKG1hc3NfZmRpdl9zdGFuZGFyZCwgbmEucm0gPSBUKSwKICBzZF9tYXNzX2ZkaXZfc3RkID0gc2QobWFzc19mZGl2X3N0YW5kYXJkLCBuYS5ybSA9IFQpLAogIAogIG1lYW5fZ2FwZV93aWR0aF9mZGl2X3N0ZCA9IG1lYW4oYmVha193aWR0aF9mZGl2X3N0YW5kYXJkLCBuYS5ybSA9IFQpLAogIG1lZGlhbl9nYXBlX3dpZHRoX2ZkaXZfc3RkID0gbWVkaWFuKGJlYWtfd2lkdGhfZmRpdl9zdGFuZGFyZCwgbmEucm0gPSBUKSwKICBzZF9nYXBlX3dpZHRoX2ZkaXZfc3RkID0gc2QoYmVha193aWR0aF9mZGl2X3N0YW5kYXJkLCBuYS5ybSA9IFQpLAogIAogIG1lYW5faGFuZHdpbmdfaW5kZXhfZmRpdl9zdGQgPSBtZWFuKGh3aV9mZGl2X3N0YW5kYXJkLCBuYS5ybSA9IFQpLAogIG1lZGlhbl9oYW5kd2luZ19pbmRleF9mZGl2X3N0ZCA9IG1lZGlhbihod2lfZmRpdl9zdGFuZGFyZCwgbmEucm0gPSBUKSwKICBzZF9oYW5kd2luZ19pbmRleF9mZGl2X3N0ZCA9IHNkKGh3aV9mZGl2X3N0YW5kYXJkLCBuYS5ybSA9IFQpCikKYGBgCgpgYGB7cn0KY29tbXVuaXR5X2RhdGFfd2l0aF9pbnRyb2R1Y3Rpb25zICU+JSBncm91cF9ieShoYXNfaW50cm9kdWNlZF9zcGVjaWVzKSAlPiUgc3VtbWFyaXNlKAogIHRvdGFsX2NpdGllcyA9IG4oKSwgCiAgCiAgbWVkaWFuX21udGRfbnJtID0gbWVkaWFuKG1udGRfbm9ybWFsLCBuYS5ybSA9IFQpLAogIG1lZGlhbl9tYXNzX2ZkaXZfbnJtID0gbWVkaWFuKG1hc3NfZmRpdl9ub3JtYWwsIG5hLnJtID0gVCksCiAgbWVkaWFuX2dhcGVfd2lkdGhfZmRpdl9ucm0gPSBtZWRpYW4oYmVha193aWR0aF9mZGl2X25vcm1hbCwgbmEucm0gPSBUKSwKICBtZWRpYW5faGFuZHdpbmdfaW5kZXhfZmRpdl9ucm0gPSBtZWRpYW4oaHdpX2ZkaXZfbm9ybWFsLCBuYS5ybSA9IFQpCikKYGBgCgpgYGB7cn0KdGVzdF9yZXF1aXJlZF92YWx1ZXMoJ0hhcyBpbnRyb2R1Y2VkJywgY29tbXVuaXR5X2RhdGFfd2l0aF9pbnRyb2R1Y3Rpb25zW2NvbW11bml0eV9kYXRhX3dpdGhfaW50cm9kdWN0aW9ucyRoYXNfaW50cm9kdWNlZF9zcGVjaWVzID09IFQsXSkKYGBgCgoKYGBge3J9CnRlc3RfcmVxdWlyZWRfdmFsdWVzKCdEb2VzIG5vdCBoYXZlIGludHJvZHVjZWQnLCBjb21tdW5pdHlfZGF0YV93aXRoX2ludHJvZHVjdGlvbnNbY29tbXVuaXR5X2RhdGFfd2l0aF9pbnRyb2R1Y3Rpb25zJGhhc19pbnRyb2R1Y2VkX3NwZWNpZXMgPT0gRixdKQpgYGAKCgojIyBNTlRECmBgYHtyfQpnZ3Bsb3QoY29tbXVuaXR5X2RhdGFfd2l0aF9pbnRyb2R1Y3Rpb25zLCBhZXMoeCA9IGhhc19pbnRyb2R1Y2VkX3NwZWNpZXMsIHkgPSBtbnRkX3N0YW5kYXJkKSkgKyBnZW9tX2JveHBsb3QoKQpgYGAKCmBgYHtyfQpnZ3Bsb3QoY29tbXVuaXR5X2RhdGFfd2l0aF9pbnRyb2R1Y3Rpb25zLCBhZXMoeCA9IGhhc19pbnRyb2R1Y2VkX3NwZWNpZXMsIHkgPSBtbnRkX25vcm1hbCkpICsgZ2VvbV9ib3hwbG90KCkKYGBgCgpgYGB7cn0Kd2lsY294LnRlc3QobW50ZF9zdGFuZGFyZCB+IGhhc19pbnRyb2R1Y2VkX3NwZWNpZXMsIGNvbW11bml0eV9kYXRhX3dpdGhfaW50cm9kdWN0aW9ucywgbmEuYWN0aW9uID0gJ25hLm9taXQnKQpgYGAKClRoZXJlIGlzIGEgc2lnbmlmaWNhbnQgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSByZXNwb25zZSBvZiBjaXRpZXMgd2l0aCBpbnRyb2R1Y2VkIHNwZWNpZXMgKDAuNTPCsTAuMjcpIGFuZCB0aG9zZSB3aXRob3V0ICgwLjQ3wrEwLjE5KSAocC12YWx1ZSA9IDAuMDIpLgoKCmBgYHtyfQp3aWxjb3gudGVzdChtbnRkX25vcm1hbCB+IGhhc19pbnRyb2R1Y2VkX3NwZWNpZXMsIGNvbW11bml0eV9kYXRhX3dpdGhfaW50cm9kdWN0aW9ucywgbmEuYWN0aW9uID0gJ25hLm9taXQnKQpgYGAKCiMjIE1hc3MgRkRpdgpgYGB7cn0KZ2dwbG90KGNvbW11bml0eV9kYXRhX3dpdGhfaW50cm9kdWN0aW9ucywgYWVzKHggPSBoYXNfaW50cm9kdWNlZF9zcGVjaWVzLCB5ID0gbWFzc19mZGl2X3N0YW5kYXJkKSkgKyBnZW9tX2JveHBsb3QoKQpgYGAKCmBgYHtyfQpnZ3Bsb3QoY29tbXVuaXR5X2RhdGFfd2l0aF9pbnRyb2R1Y3Rpb25zLCBhZXMoeCA9IGhhc19pbnRyb2R1Y2VkX3NwZWNpZXMsIHkgPSBtYXNzX2ZkaXZfbm9ybWFsKSkgKyBnZW9tX2JveHBsb3QoKQpgYGAKCmBgYHtyfQp3aWxjb3gudGVzdChtYXNzX2ZkaXZfc3RhbmRhcmQgfiBoYXNfaW50cm9kdWNlZF9zcGVjaWVzLCBjb21tdW5pdHlfZGF0YV93aXRoX2ludHJvZHVjdGlvbnMsIG5hLmFjdGlvbiA9ICduYS5vbWl0JykKYGBgClRoZXJlIGlzIGEgc2lnbmlmaWNhbnQgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSByZXNwb25zZSBvZiBjaXRpZXMgd2l0aCBpbnRyb2R1Y2VkIHNwZWNpZXMgKDAuNTfCsTAuMjcpIGFuZCB0aG9zZSB3aXRob3V0ICgwLjczwrEwLjI0KSAocCA8IDAuMDAwMSkKCmBgYHtyfQp3aWxjb3gudGVzdChtYXNzX2ZkaXZfbm9ybWFsIH4gaGFzX2ludHJvZHVjZWRfc3BlY2llcywgY29tbXVuaXR5X2RhdGFfd2l0aF9pbnRyb2R1Y3Rpb25zLCBuYS5hY3Rpb24gPSAnbmEub21pdCcpCmBgYAoKIyMgQmVhayBHYXBlIEZEaXYKYGBge3J9CmdncGxvdChjb21tdW5pdHlfZGF0YV93aXRoX2ludHJvZHVjdGlvbnMsIGFlcyh4ID0gaGFzX2ludHJvZHVjZWRfc3BlY2llcywgeSA9IGJlYWtfd2lkdGhfZmRpdl9zdGFuZGFyZCkpICsgZ2VvbV9ib3hwbG90KCkKYGBgCgpgYGB7cn0KZ2dwbG90KGNvbW11bml0eV9kYXRhX3dpdGhfaW50cm9kdWN0aW9ucywgYWVzKHggPSBoYXNfaW50cm9kdWNlZF9zcGVjaWVzLCB5ID0gYmVha193aWR0aF9mZGl2X25vcm1hbCkpICsgZ2VvbV9ib3hwbG90KCkKYGBgCgpgYGB7cn0Kd2lsY294LnRlc3QoYmVha193aWR0aF9mZGl2X3N0YW5kYXJkIH4gaGFzX2ludHJvZHVjZWRfc3BlY2llcywgY29tbXVuaXR5X2RhdGFfd2l0aF9pbnRyb2R1Y3Rpb25zLCBuYS5hY3Rpb24gPSAnbmEub21pdCcpCmBgYApUaGVyZSBpcyBOT1QgYSBzaWduaWZpY2FudCBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIHJlc3BvbnNlIG9mIGNpdGllcyB3aXRoIGludHJvZHVjZWQgc3BlY2llcyAoMC42McKxMC4zMCkgYW5kIHRob3NlIHdpdGhvdXQgKDAuNTbCsTAuMjcpCgpgYGB7cn0Kd2lsY294LnRlc3QoYmVha193aWR0aF9mZGl2X25vcm1hbCB+IGhhc19pbnRyb2R1Y2VkX3NwZWNpZXMsIGNvbW11bml0eV9kYXRhX3dpdGhfaW50cm9kdWN0aW9ucywgbmEuYWN0aW9uID0gJ25hLm9taXQnKQpgYGAKCiMjIEhXSSBGRGl2CmBgYHtyfQpnZ3Bsb3QoY29tbXVuaXR5X2RhdGFfd2l0aF9pbnRyb2R1Y3Rpb25zLCBhZXMoeCA9IGhhc19pbnRyb2R1Y2VkX3NwZWNpZXMsIHkgPSBod2lfZmRpdl9zdGFuZGFyZCkpICsgZ2VvbV9ib3hwbG90KCkKYGBgCgpgYGB7cn0KZ2dwbG90KGNvbW11bml0eV9kYXRhX3dpdGhfaW50cm9kdWN0aW9ucywgYWVzKHggPSBoYXNfaW50cm9kdWNlZF9zcGVjaWVzLCB5ID0gaHdpX2ZkaXZfbm9ybWFsKSkgKyBnZW9tX2JveHBsb3QoKQpgYGAKCmBgYHtyfQp3aWxjb3gudGVzdChod2lfZmRpdl9zdGFuZGFyZCB+IGhhc19pbnRyb2R1Y2VkX3NwZWNpZXMsIGNvbW11bml0eV9kYXRhX3dpdGhfaW50cm9kdWN0aW9ucywgbmEuYWN0aW9uID0gJ25hLm9taXQnKQpgYGAKVGhlcmUgaXMgYSBzaWduaWZpY2FudCBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIHJlc3BvbnNlIG9mIGNpdGllcyB3aXRoIGludHJvZHVjZWQgc3BlY2llcyAoMC40OcKxMC4zMCkgYW5kIHRob3NlIHdpdGhvdXQgKDAuNznCsTAuMjEpIChwIDwgMC4wMDAxKQoKYGBge3J9CndpbGNveC50ZXN0KGh3aV9mZGl2X25vcm1hbCB+IGhhc19pbnRyb2R1Y2VkX3NwZWNpZXMsIGNvbW11bml0eV9kYXRhX3dpdGhfaW50cm9kdWN0aW9ucywgbmEuYWN0aW9uID0gJ25hLm9taXQnKQpgYGAKCiMjIFdoYXQgcHJvcG9ydGlvbiBvZiBjaXRpZXMgaW4gZWFjaCByZWFsbSBoYXZlIGludHJvZHVjZWQgc3BlY2llcwpgYGB7cn0KY29tbXVuaXR5X2RhdGFfd2l0aF9pbnRyb2R1Y3Rpb25zICU+JSBsZWZ0X2pvaW4oY2l0eV90b19yZWFsbSkgJT4lCiAgZ3JvdXBfYnkoY29yZV9yZWFsbSkgJT4lCiAgc3VtbWFyaXNlKAogICAgdG90YWxfY2l0aWVzID0gbigpLCAKICAgIHRvdGFsX2NpdGllc193aXRoX2ludHJvZHVjZWQgPSBzdW0oaGFzX2ludHJvZHVjZWRfc3BlY2llcyksIAogICAgdG90YWxfY2l0aWVzX3dpdGhvdXRfaW50cm9kdWNlZCA9IG4oKSAtIHN1bShoYXNfaW50cm9kdWNlZF9zcGVjaWVzKSkgJT4lCiAgYXJyYW5nZShjb3JlX3JlYWxtKQpgYGAKCiMjIEFyZSBhbnkgaW50cm9kdWNlZCBzcGVjaWVzIG5vdCBhbHNvIHByZXNlbnQgaW4gYSBjaXR5PwpgYGB7cn0KY29tbXVuaXRpZXMgJT4lIAogIGZpbHRlcihvcmlnaW4gPT0gJ0ludHJvZHVjZWQnKSAlPiUgCiAgZmlsdGVyKHJlbGF0aXZlX2FidW5kYW5jZV9wcm94eSA8IDAuMSkKYGBgCgojIyBXaGF0J3MgdGhlIGF2ZXJhZ2UgcmVsYXRpdmUgYWJ1bmRhbmNlIG9mIGludHJvZHVjZWQgc3BlY2llcyBjb21wYXJlZCB0byBuYXRpdmUKYGBge3J9CmNvbW11bml0aWVzICU+JSAKICBncm91cF9ieShvcmlnaW4pICU+JSAKICBzdW1tYXJpc2UoYXZlcmFnZV9yZWxhdGl2ZV9hYnVuZGFuY2UgPSBtZWFuKHJlbGF0aXZlX2FidW5kYW5jZV9wcm94eSkpCmBgYAoKYGBge3J9CmNvbW11bml0aWVzICU+JSAKICBncm91cF9ieShvcmlnaW4pICU+JSAKICBmaWx0ZXIocmVsYXRpdmVfYWJ1bmRhbmNlX3Byb3h5ID4gMCkgJT4lCiAgc3VtbWFyaXNlKGF2ZXJhZ2VfcmVsYXRpdmVfYWJ1bmRhbmNlID0gbWVhbihyZWxhdGl2ZV9hYnVuZGFuY2VfcHJveHkpKQpgYGAKCmBgYHtyfQpjb21tdW5pdGllcyAlPiUgCiAgZ3JvdXBfYnkob3JpZ2luKSAlPiUgCiAgc3VtbWFyaXNlKGF2ZXJhZ2VfcmVsYXRpdmVfYWJ1bmRhbmNlID0gbWVhbihyZWxhdGl2ZV9hYnVuZGFuY2VfcHJveHkpKQpgYGAKCiMgQ3JlYXRlIGFuYWx5c2lzIGRhdGEgZnJhbWUgZm9yIG1vZGVsbGluZwpgYGB7cn0KZ2VvZ3JhcGh5ID0gcmVhZF9jc3YoZmlsZW5hbWUoQ0lUWV9EQVRBX09VVFBVVF9ESVIsICdnZW9ncmFwaHkuY3N2JykpCm5hbWVzKGdlb2dyYXBoeSkKYGBgCgpgYGB7cn0KYW5hbHlzaXNfZGF0YSA9IGNvbW11bml0eV9kYXRhX3dpdGhfcmVhbG1bLGMoJ2NpdHlfaWQnLCAKICAgICAgICdtbnRkX3N0YW5kYXJkJywgJ21hc3NfZmRpdl9zdGFuZGFyZCcsICdiZWFrX3dpZHRoX2ZkaXZfc3RhbmRhcmQnLCAnaHdpX2ZkaXZfc3RhbmRhcmQnLAogICAgICAgJ21udGRfbm9ybWFsJywgJ21hc3NfZmRpdl9ub3JtYWwnLCAnYmVha193aWR0aF9mZGl2X25vcm1hbCcsICdod2lfZmRpdl9ub3JtYWwnLAogICAgICAgJ2NvcmVfcmVhbG0nKV0gJT4lIAogIGxlZnRfam9pbihjaXR5X3BvaW50c1ssYygnY2l0eV9pZCcsICdsYXRpdHVkZScsICdsb25naXR1ZGUnKV0pICU+JQogIGxlZnRfam9pbihjb21tdW5pdHlfZGF0YV93aXRoX2ludHJvZHVjdGlvbnNbLGMoJ2NpdHlfaWQnLCAnaGFzX2ludHJvZHVjZWRfc3BlY2llcycpXSkgJT4lCiAgbGVmdF9qb2luKGdlb2dyYXBoeSkgJT4lCiAgbGVmdF9qb2luKHNwYXRpYWxfdmFyKQoKYW5hbHlzaXNfZGF0YSRhYnNfbGF0aXR1ZGUgPSBhYnMoYW5hbHlzaXNfZGF0YSRsYXRpdHVkZSkKYW5hbHlzaXNfZGF0YSRjb3JlX3JlYWxtID0gZmFjdG9yKGFuYWx5c2lzX2RhdGEkY29yZV9yZWFsbSwgbGV2ZWxzID0gYygnUGFsZWFyY3RpYycsICdOZWFyY3RpYycsICdOZW90cm9waWMnLCAnQWZyb3Ryb3BpYycsICdJbmRvbWFsYXlhbicsICdBdXN0cmFsYXNpYScsICdPY2VhbmlhJykpCmFuYWx5c2lzX2RhdGEkaGFzX2ludHJvZHVjZWRfc3BlY2llcyA9IGZhY3RvcihhbmFseXNpc19kYXRhJGhhc19pbnRyb2R1Y2VkX3NwZWNpZXMsIGxldmVsID0gYygnRkFMU0UnLCAnVFJVRScpLCBsYWJlbHMgPSBjKCdObyBpbnRyb2R1Y2VkIHNwZWNpZXMnLCAnSW50cm9kdWNlZCBzcGVjaWVzJykpCmBgYAoKYGBge3J9Cm1vZGVsX2RhdGEgPSBmdW5jdGlvbihkZiwgZGVwZW5kYW50X3ZhcikgewogIGRmWyxjKGRlcGVuZGFudF92YXIsICdjb3JlX3JlYWxtJywgJ2Fic19sYXRpdHVkZScsICdsb25naXR1ZGUnLCAnaGFzX2ludHJvZHVjZWRfc3BlY2llcycsICdjaXR5X2F2Z19uZHZpJywgJ2NpdHlfYXZnX2VsZXZhdGlvbicsICdjaXR5X2F2Z190ZW1wJywgJ2NpdHlfYXZnX21pbl9tb250aGx5X3RlbXAnLCAnY2l0eV9hdmdfbWF4X21vbnRobHlfdGVtcCcsICdjaXR5X2F2Z19tb250aGx5X3RlbXAnLCAnY2l0eV9hdmdfcmFpbmZhbGwnLCAnY2l0eV9hdmdfbWF4X21vbnRobHlfcmFpbmZhbGwnLCAnY2l0eV9hdmdfbWluX21vbnRobHlfcmFpbmZhbGwnLCAnY2l0eV9hdmdfc29pbF9tb2lzdHVyZScsICdjaXR5X21heF9lbGV2JywgJ2NpdHlfbWluX2VsZXYnLCAnY2l0eV9lbGV2X3JhbmdlJywgJ3JlZ2lvbl8yMGttX2F2Z19uZHZpJywgJ3JlZ2lvbl8yMGttX2F2Z19lbGV2YXRpb24nLCAncmVnaW9uXzIwa21fYXZnX3NvaWxfbW9pc3R1cmUnLCAncmVnaW9uXzIwa21fbWF4X2VsZXYnLCAncmVnaW9uXzIwa21fbWluX2VsZXYnLCAncmVnaW9uXzIwa21fZWxldl9yYW5nZScsICdyZWdpb25fNTBrbV9hdmdfbmR2aScsICdyZWdpb25fNTBrbV9hdmdfZWxldmF0aW9uJywgJ3JlZ2lvbl81MGttX2F2Z19zb2lsX21vaXN0dXJlJywgJ3JlZ2lvbl81MGttX21heF9lbGV2JywgJ3JlZ2lvbl81MGttX21pbl9lbGV2JywgJ3JlZ2lvbl81MGttX2VsZXZfcmFuZ2UnKV0KfQptb2RlbF9kYXRhKGFuYWx5c2lzX2RhdGEsICdtbnRkX3N0YW5kYXJkJykKbW9kZWxfZGF0YShhbmFseXNpc19kYXRhLCAnbW50ZF9ub3JtYWwnKQpgYGAKCmBgYHtyfQpuYW1lcyhhbmFseXNpc19kYXRhKQpgYGAKCiMjIE5NRFMgU3BhdGlhbCBIZWxwZXJzCmBgYHtyfQphbmFseXNpc19kYXRhX25tZHNfY29vcmRzID0gYW5hbHlzaXNfZGF0YVssYygnTk1EUzEnLCAnTk1EUzInKV0KY29vcmRpbmF0ZXMoYW5hbHlzaXNfZGF0YV9ubWRzX2Nvb3JkcykgID0gfiBOTURTMSArIE5NRFMyCgphbmFseXNpc19kYXRhX25tZHNfbmVhcm5laWdoIDwtIGtuZWFybmVpZ2goYW5hbHlzaXNfZGF0YV9ubWRzX2Nvb3JkcykKYW5hbHlzaXNfZGF0YV9ubWRzX25laWdoYm91cnMgPC0ga25uMm5iKGFuYWx5c2lzX2RhdGFfbm1kc19uZWFybmVpZ2gpCmBgYAoKIyMjIFBvbHlnb25zIGFyb3VuZCByZWFsbXMgaW4gTk1EUyBwbG90CmBgYHtyfQpjaXRpZXNfdG9fcmVhbG1zX25tZHMgPSByZWFkX2NzdihmaWxlbmFtZShDSVRZX0RBVEFfT1VUUFVUX0RJUiwgJ3JlYWxtcy5jc3YnKSkgJT4lIGxlZnRfam9pbihhbmFseXNpc19kYXRhKSAlPiUgZmlsdGVyKCFpcy5uYShOTURTMSkpCnVuaXF1ZShjaXRpZXNfdG9fcmVhbG1zX25tZHMkY29yZV9yZWFsbSkKcmVhbG1fbm1kc19uZWFydGljX3BvbHlnb24gPSBjaXRpZXNfdG9fcmVhbG1zX25tZHMgJT4lIGZpbHRlcihjb3JlX3JlYWxtID09ICdOZWFyY3RpYycpICU+JSBzbGljZShjaHVsbChOTURTMSwgTk1EUzIpKQpyZWFsbV9ubWRzX25lb3Ryb3BpY19wb2x5Z29uID0gY2l0aWVzX3RvX3JlYWxtc19ubWRzICU+JSBmaWx0ZXIoY29yZV9yZWFsbSA9PSAnTmVvdHJvcGljJykgJT4lIHNsaWNlKGNodWxsKE5NRFMxLCBOTURTMikpCnJlYWxtX25tZHNfcGFsZWFyY3RpY19wb2x5Z29uID0gY2l0aWVzX3RvX3JlYWxtc19ubWRzICU+JSBmaWx0ZXIoY29yZV9yZWFsbSA9PSAnUGFsZWFyY3RpYycpICU+JSBzbGljZShjaHVsbChOTURTMSwgTk1EUzIpKQpyZWFsbV9ubWRzX2Fmcm90cm9waWNfcG9seWdvbiA9IGNpdGllc190b19yZWFsbXNfbm1kcyAlPiUgZmlsdGVyKGNvcmVfcmVhbG0gPT0gJ0Fmcm90cm9waWMnKSAlPiUgc2xpY2UoY2h1bGwoTk1EUzEsIE5NRFMyKSkKcmVhbG1fbm1kc19pbmRvbWFsYXlhbl9wb2x5Z29uID0gY2l0aWVzX3RvX3JlYWxtc19ubWRzICU+JSBmaWx0ZXIoY29yZV9yZWFsbSA9PSAnSW5kb21hbGF5YW4nKSAlPiUgc2xpY2UoY2h1bGwoTk1EUzEsIE5NRFMyKSkKcmVhbG1fbm1kc19hdXN0cmFsYXNpYV9wb2x5Z29uID0gY2l0aWVzX3RvX3JlYWxtc19ubWRzICU+JSBmaWx0ZXIoY29yZV9yZWFsbSA9PSAnQXVzdHJhbGFzaWEnKSAlPiUgc2xpY2UoY2h1bGwoTk1EUzEsIE5NRFMyKSkKCnBvbHlnb25fbGluZV90eXBlID0gJ2Rhc2hlZCcKcG9seWdvbl9saW5ld2lkdGggPSAwLjQKCndpdGhfcmVhbG1zX25tZHMgPSBmdW5jdGlvbihnKSB7CiAgZyArIAogICAgZ2VvbV9wb2x5Z29uKGRhdGEgPSByZWFsbV9ubWRzX25lYXJ0aWNfcG9seWdvbiwgbGluZXdpZHRoID0gcG9seWdvbl9saW5ld2lkdGgsIGxpbmV0eXBlID0gcG9seWdvbl9saW5lX3R5cGUsIGFscGhhID0gMCkgKwogICAgZ2VvbV9wb2x5Z29uKGRhdGEgPSByZWFsbV9ubWRzX25lb3Ryb3BpY19wb2x5Z29uLCBsaW5ld2lkdGggPSBwb2x5Z29uX2xpbmV3aWR0aCwgbGluZXR5cGUgPSBwb2x5Z29uX2xpbmVfdHlwZSwgYWxwaGEgPSAwKSArCiAgICBnZW9tX3BvbHlnb24oZGF0YSA9IHJlYWxtX25tZHNfcGFsZWFyY3RpY19wb2x5Z29uLCBsaW5ld2lkdGggPSBwb2x5Z29uX2xpbmV3aWR0aCwgbGluZXR5cGUgPSBwb2x5Z29uX2xpbmVfdHlwZSwgYWxwaGEgPSAwKSArCiAgICBnZW9tX3BvbHlnb24oZGF0YSA9IHJlYWxtX25tZHNfYWZyb3Ryb3BpY19wb2x5Z29uLCBsaW5ld2lkdGggPSBwb2x5Z29uX2xpbmV3aWR0aCwgbGluZXR5cGUgPSBwb2x5Z29uX2xpbmVfdHlwZSwgYWxwaGEgPSAwKSArCiAgICBnZW9tX3BvbHlnb24oZGF0YSA9IHJlYWxtX25tZHNfaW5kb21hbGF5YW5fcG9seWdvbiwgbGluZXdpZHRoID0gcG9seWdvbl9saW5ld2lkdGgsIGxpbmV0eXBlID0gcG9seWdvbl9saW5lX3R5cGUsIGFscGhhID0gMCkgKwogICAgZ2VvbV9wb2x5Z29uKGRhdGEgPSByZWFsbV9ubWRzX2F1c3RyYWxhc2lhX3BvbHlnb24sIGxpbmV3aWR0aCA9IHBvbHlnb25fbGluZXdpZHRoLCBsaW5ldHlwZSA9IHBvbHlnb25fbGluZV90eXBlLCBhbHBoYSA9IDApCn0KYGBgCgojIyBMYXQgTG9uZyBTcGF0aWFsIEhlbHBlcnMKYGBge3J9CmFuYWx5c2lzX2RhdGFfbGF0bG9uZ19jb29yZHMgPSBhbmFseXNpc19kYXRhWyxjKCdsb25naXR1ZGUnLCAnbGF0aXR1ZGUnKV0KY29vcmRpbmF0ZXMoYW5hbHlzaXNfZGF0YV9sYXRsb25nX2Nvb3JkcykgID0gfiBsb25naXR1ZGUgKyBsYXRpdHVkZQoKYW5hbHlzaXNfZGF0YV9jb29yZHNfbmVhcm5laWdoIDwtIGtuZWFybmVpZ2goYW5hbHlzaXNfZGF0YV9sYXRsb25nX2Nvb3JkcywgbG9uZ2xhdCA9IFRSVUUpCmFuYWx5c2lzX2RhdGFfbmVpZ2hib3VycyA8LSBrbm4ybmIoYW5hbHlzaXNfZGF0YV9jb29yZHNfbmVhcm5laWdoKQpgYGAKCiMjIyBQb2x5Z29ucyBhcm91bmQgcmVhbG1zIGluIGxhdCBsb25nIHBsb3QKYGBge3J9CmNpdGllc190b19yZWFsbXNfbGF0bG9uZyA9IHJlYWRfY3N2KGZpbGVuYW1lKENJVFlfREFUQV9PVVRQVVRfRElSLCAncmVhbG1zLmNzdicpKSAlPiUgbGVmdF9qb2luKGFuYWx5c2lzX2RhdGEpICU+JSBmaWx0ZXIoIWlzLm5hKGxhdGl0dWRlKSkKdW5pcXVlKGNpdGllc190b19yZWFsbXNfbGF0bG9uZyRjb3JlX3JlYWxtKQpyZWFsbV9sYXRsb25nX25lYXJ0aWNfcG9seWdvbiA9IGNpdGllc190b19yZWFsbXNfbGF0bG9uZyAlPiUgZmlsdGVyKGNvcmVfcmVhbG0gPT0gJ05lYXJjdGljJykgJT4lIHNsaWNlKGNodWxsKGxhdGl0dWRlLCBsb25naXR1ZGUpKQpyZWFsbV9sYXRsb25nX25lb3Ryb3BpY19wb2x5Z29uID0gY2l0aWVzX3RvX3JlYWxtc19sYXRsb25nICU+JSBmaWx0ZXIoY29yZV9yZWFsbSA9PSAnTmVvdHJvcGljJykgJT4lIHNsaWNlKGNodWxsKGxhdGl0dWRlLCBsb25naXR1ZGUpKQpyZWFsbV9sYXRsb25nX3BhbGVhcmN0aWNfcG9seWdvbiA9IGNpdGllc190b19yZWFsbXNfbGF0bG9uZyAlPiUgZmlsdGVyKGNvcmVfcmVhbG0gPT0gJ1BhbGVhcmN0aWMnKSAlPiUgc2xpY2UoY2h1bGwobGF0aXR1ZGUsIGxvbmdpdHVkZSkpCnJlYWxtX2xhdGxvbmdfYWZyb3Ryb3BpY19wb2x5Z29uID0gY2l0aWVzX3RvX3JlYWxtc19sYXRsb25nICU+JSBmaWx0ZXIoY29yZV9yZWFsbSA9PSAnQWZyb3Ryb3BpYycpICU+JSBzbGljZShjaHVsbChsYXRpdHVkZSwgbG9uZ2l0dWRlKSkKcmVhbG1fbGF0bG9uZ19pbmRvbWFsYXlhbl9wb2x5Z29uID0gY2l0aWVzX3RvX3JlYWxtc19sYXRsb25nICU+JSBmaWx0ZXIoY29yZV9yZWFsbSA9PSAnSW5kb21hbGF5YW4nKSAlPiUgc2xpY2UoY2h1bGwobGF0aXR1ZGUsIGxvbmdpdHVkZSkpCnJlYWxtX2xhdGxvbmdfYXVzdHJhbGFzaWFfcG9seWdvbiA9IGNpdGllc190b19yZWFsbXNfbGF0bG9uZyAlPiUgZmlsdGVyKGNvcmVfcmVhbG0gPT0gJ0F1c3RyYWxhc2lhJykgJT4lIHNsaWNlKGNodWxsKGxhdGl0dWRlLCBsb25naXR1ZGUpKQoKd2l0aF9yZWFsbXNfbGF0bG9uZyA9IGZ1bmN0aW9uKGcpIHsKICBnICsgCiAgICBnZW9tX3BvbHlnb24oZGF0YSA9IHJlYWxtX2xhdGxvbmdfbmVhcnRpY19wb2x5Z29uLCBsaW5ld2lkdGggPSBwb2x5Z29uX2xpbmV3aWR0aCwgbGluZXR5cGUgPSBwb2x5Z29uX2xpbmVfdHlwZSwgYWxwaGEgPSAwKSArCiAgICBnZW9tX3BvbHlnb24oZGF0YSA9IHJlYWxtX2xhdGxvbmdfbmVvdHJvcGljX3BvbHlnb24sIGxpbmV3aWR0aCA9IHBvbHlnb25fbGluZXdpZHRoLCBsaW5ldHlwZSA9IHBvbHlnb25fbGluZV90eXBlLCBhbHBoYSA9IDApICsKICAgIGdlb21fcG9seWdvbihkYXRhID0gcmVhbG1fbGF0bG9uZ19wYWxlYXJjdGljX3BvbHlnb24sIGxpbmV3aWR0aCA9IHBvbHlnb25fbGluZXdpZHRoLCBsaW5ldHlwZSA9IHBvbHlnb25fbGluZV90eXBlLCBhbHBoYSA9IDApICsKICAgIGdlb21fcG9seWdvbihkYXRhID0gcmVhbG1fbGF0bG9uZ19hZnJvdHJvcGljX3BvbHlnb24sIGxpbmV3aWR0aCA9IHBvbHlnb25fbGluZXdpZHRoLCBsaW5ldHlwZSA9IHBvbHlnb25fbGluZV90eXBlLCBhbHBoYSA9IDApICsKICAgIGdlb21fcG9seWdvbihkYXRhID0gcmVhbG1fbGF0bG9uZ19pbmRvbWFsYXlhbl9wb2x5Z29uLCBsaW5ld2lkdGggPSBwb2x5Z29uX2xpbmV3aWR0aCwgbGluZXR5cGUgPSBwb2x5Z29uX2xpbmVfdHlwZSwgYWxwaGEgPSAwKSArCiAgICBnZW9tX3BvbHlnb24oZGF0YSA9IHJlYWxtX2xhdGxvbmdfYXVzdHJhbGFzaWFfcG9seWdvbiwgbGluZXdpZHRoID0gcG9seWdvbl9saW5ld2lkdGgsIGxpbmV0eXBlID0gcG9seWdvbl9saW5lX3R5cGUsIGFscGhhID0gMCkKfQpgYGAKCiMgQ2hlY2sgZm9yIHNwYXRpYWwgYXV0b2NvcnJlbGF0aW9uCgojIyBNTlRECgojIyMgTGF0L0xvbmcKYGBge3J9CndpdGhfcmVhbG1zX2xhdGxvbmcoZ2dwbG90KGFuYWx5c2lzX2RhdGEsIGFlcyh4ID0gbGF0aXR1ZGUsIHkgPSBsb25naXR1ZGUsIGNvbG91ciA9IG1udGRfc3RhbmRhcmQpKSArIGdlb21fcG9pbnQoKSArIHN0YW5kYXJkaXNlZF9jb2xvdXJzX3NjYWxlICsgbGFicyhjb2xvdXIgPSAiU3RhbmRhcmRpc2VkIHJlc3BvbnNlIikpCmBgYAoKYGBge3J9Cm1vcmFuLnRlc3QoYW5hbHlzaXNfZGF0YSRtbnRkX3N0YW5kYXJkLCBuYjJsaXN0dyhhbmFseXNpc19kYXRhX25laWdoYm91cnMpKQpgYGAKYGBge3J9CndpdGhfcmVhbG1zX2xhdGxvbmcoZ2dwbG90KGFuYWx5c2lzX2RhdGEsIGFlcyh4ID0gbGF0aXR1ZGUsIHkgPSBsb25naXR1ZGUsIGNvbG91ciA9IG1udGRfbm9ybWFsKSkgKyBnZW9tX3BvaW50KCkgKyBub3JtYWxpc2VkX2NvbG91cnNfc2NhbGUgKyBsYWJzKGNvbG91ciA9ICJOb3JtYWxpc2VkIHJlc3BvbnNlIikpCmBgYAoKYGBge3J9Cm1vcmFuLnRlc3QoYW5hbHlzaXNfZGF0YSRtbnRkX25vcm1hbCwgbmIybGlzdHcoYW5hbHlzaXNfZGF0YV9uZWlnaGJvdXJzKSkKYGBgCgojIyMgTk1EUwpgYGB7cn0Kd2l0aF9yZWFsbXNfbm1kcyhnZ3Bsb3QoYW5hbHlzaXNfZGF0YSwgYWVzKHggPSBOTURTMSwgeSA9IE5NRFMyLCBjb2xvdXIgPSBtbnRkX3N0YW5kYXJkKSkgKyBnZW9tX3BvaW50KCkgKyBzdGFuZGFyZGlzZWRfY29sb3Vyc19zY2FsZSArIGxhYnMoY29sb3VyID0gIlN0YW5kYXJkaXNlZCByZXNwb25zZSIpKQpgYGAKCmBgYHtyfQptb3Jhbi50ZXN0KGFuYWx5c2lzX2RhdGEkbW50ZF9zdGFuZGFyZCwgbmIybGlzdHcoYW5hbHlzaXNfZGF0YV9ubWRzX25laWdoYm91cnMpKQpgYGAKCmBgYHtyfQp3aXRoX3JlYWxtc19ubWRzKGdncGxvdChhbmFseXNpc19kYXRhLCBhZXMoeCA9IE5NRFMxLCB5ID0gTk1EUzIsIGNvbG91ciA9IG1udGRfbm9ybWFsKSkgKyBnZW9tX3BvaW50KCkgKyBzdGFuZGFyZGlzZWRfY29sb3Vyc19zY2FsZSArIGxhYnMoY29sb3VyID0gIk5vcm1hbGlzZWQgcmVzcG9uc2UiKSkKYGBgCgpgYGB7cn0KbW9yYW4udGVzdChhbmFseXNpc19kYXRhJG1udGRfbm9ybWFsLCBuYjJsaXN0dyhhbmFseXNpc19kYXRhX25tZHNfbmVpZ2hib3VycykpCmBgYAoKIyMgRkRpdiBCZWFrIFdpZHRoCgojIyMgTGF0L0xvbmcKYGBge3J9CndpdGhfcmVhbG1zX2xhdGxvbmcoZ2dwbG90KGFuYWx5c2lzX2RhdGEsIGFlcyh4ID0gbGF0aXR1ZGUsIHkgPSBsb25naXR1ZGUsIGNvbG91ciA9IGJlYWtfd2lkdGhfZmRpdl9zdGFuZGFyZCkpICsgZ2VvbV9wb2ludCgpICsgc3RhbmRhcmRpc2VkX2NvbG91cnNfc2NhbGUgKyBsYWJzKGNvbG91ciA9ICJTdGFuZGFyZGlzZWQgcmVzcG9uc2UiKSkKYGBgCgpgYGB7cn0KbW9yYW4udGVzdChhbmFseXNpc19kYXRhJGJlYWtfd2lkdGhfZmRpdl9zdGFuZGFyZCwgbmIybGlzdHcoYW5hbHlzaXNfZGF0YV9uZWlnaGJvdXJzKSkKYGBgCgpgYGB7cn0Kd2l0aF9yZWFsbXNfbGF0bG9uZyhnZ3Bsb3QoYW5hbHlzaXNfZGF0YSwgYWVzKHggPSBsYXRpdHVkZSwgeSA9IGxvbmdpdHVkZSwgY29sb3VyID0gYmVha193aWR0aF9mZGl2X25vcm1hbCkpICsgZ2VvbV9wb2ludCgpICsgc3RhbmRhcmRpc2VkX2NvbG91cnNfc2NhbGUgKyBsYWJzKGNvbG91ciA9ICJOb3JtYWxpc2VkIHJlc3BvbnNlIikpCmBgYAoKYGBge3J9Cm1vcmFuLnRlc3QoYW5hbHlzaXNfZGF0YSRiZWFrX3dpZHRoX2ZkaXZfbm9ybWFsLCBuYjJsaXN0dyhhbmFseXNpc19kYXRhX25laWdoYm91cnMpKQpgYGAKCiMjIyBOTURTCmBgYHtyfQp3aXRoX3JlYWxtc19ubWRzKGdncGxvdChhbmFseXNpc19kYXRhLCBhZXMoeCA9IE5NRFMxLCB5ID0gTk1EUzIsIGNvbG91ciA9IGJlYWtfd2lkdGhfZmRpdl9zdGFuZGFyZCkpICsgZ2VvbV9wb2ludCgpICsgc3RhbmRhcmRpc2VkX2NvbG91cnNfc2NhbGUgKyBsYWJzKGNvbG91ciA9ICJTdGFuZGFyZGlzZWQgcmVzcG9uc2UiKSkKYGBgCgpgYGB7cn0KbW9yYW4udGVzdChhbmFseXNpc19kYXRhJGJlYWtfd2lkdGhfZmRpdl9zdGFuZGFyZCwgbmIybGlzdHcoYW5hbHlzaXNfZGF0YV9ubWRzX25laWdoYm91cnMpKQpgYGAKCmBgYHtyfQp3aXRoX3JlYWxtc19ubWRzKGdncGxvdChhbmFseXNpc19kYXRhLCBhZXMoeCA9IE5NRFMxLCB5ID0gTk1EUzIsIGNvbG91ciA9IGJlYWtfd2lkdGhfZmRpdl9ub3JtYWwpKSArIGdlb21fcG9pbnQoKSArIHN0YW5kYXJkaXNlZF9jb2xvdXJzX3NjYWxlICsgbGFicyhjb2xvdXIgPSAiTm9ybWFsaXNlZCByZXNwb25zZSIpKQpgYGAKCmBgYHtyfQptb3Jhbi50ZXN0KGFuYWx5c2lzX2RhdGEkYmVha193aWR0aF9mZGl2X25vcm1hbCwgbmIybGlzdHcoYW5hbHlzaXNfZGF0YV9ubWRzX25laWdoYm91cnMpKQpgYGAKCiMjIEZEaXYgSFdJCgojIyMgTGF0L0xvbmcKYGBge3J9CndpdGhfcmVhbG1zX2xhdGxvbmcoZ2dwbG90KGFuYWx5c2lzX2RhdGEsIGFlcyh4ID0gbGF0aXR1ZGUsIHkgPSBsb25naXR1ZGUsIGNvbG91ciA9IGh3aV9mZGl2X3N0YW5kYXJkKSkgKyBnZW9tX3BvaW50KCkgKyBzdGFuZGFyZGlzZWRfY29sb3Vyc19zY2FsZSArIGxhYnMoY29sb3VyID0gIlN0YW5kYXJkaXNlZCByZXNwb25zZSIpKQpgYGAKCmBgYHtyfQptb3Jhbi50ZXN0KGFuYWx5c2lzX2RhdGEkaHdpX2ZkaXZfc3RhbmRhcmQsIG5iMmxpc3R3KGFuYWx5c2lzX2RhdGFfbmVpZ2hib3VycykpCmBgYAoKYGBge3J9CndpdGhfcmVhbG1zX2xhdGxvbmcoZ2dwbG90KGFuYWx5c2lzX2RhdGEsIGFlcyh4ID0gbGF0aXR1ZGUsIHkgPSBsb25naXR1ZGUsIGNvbG91ciA9IGh3aV9mZGl2X25vcm1hbCkpICsgZ2VvbV9wb2ludCgpICsgc3RhbmRhcmRpc2VkX2NvbG91cnNfc2NhbGUgKyBsYWJzKGNvbG91ciA9ICJOb3JtYWxpc2VkIHJlc3BvbnNlIikpCmBgYAoKYGBge3J9Cm1vcmFuLnRlc3QoYW5hbHlzaXNfZGF0YSRod2lfZmRpdl9ub3JtYWwsIG5iMmxpc3R3KGFuYWx5c2lzX2RhdGFfbmVpZ2hib3VycykpCmBgYAoKIyMjIE5NRFMKYGBge3J9CndpdGhfcmVhbG1zX25tZHMoZ2dwbG90KGFuYWx5c2lzX2RhdGEsIGFlcyh4ID0gTk1EUzEsIHkgPSBOTURTMiwgY29sb3VyID0gaHdpX2ZkaXZfc3RhbmRhcmQpKSArIGdlb21fcG9pbnQoKSArIHN0YW5kYXJkaXNlZF9jb2xvdXJzX3NjYWxlICsgbGFicyhjb2xvdXIgPSAiU3RhbmRhcmRpc2VkIHJlc3BvbnNlIikpCmBgYAoKYGBge3J9Cm1vcmFuLnRlc3QoYW5hbHlzaXNfZGF0YSRod2lfZmRpdl9zdGFuZGFyZCwgbmIybGlzdHcoYW5hbHlzaXNfZGF0YV9ubWRzX25laWdoYm91cnMpKQpgYGAKCmBgYHtyfQp3aXRoX3JlYWxtc19ubWRzKGdncGxvdChhbmFseXNpc19kYXRhLCBhZXMoeCA9IE5NRFMxLCB5ID0gTk1EUzIsIGNvbG91ciA9IGh3aV9mZGl2X25vcm1hbCkpICsgZ2VvbV9wb2ludCgpICsgc3RhbmRhcmRpc2VkX2NvbG91cnNfc2NhbGUgKyBsYWJzKGNvbG91ciA9ICJOb3JtYWxpc2VkIHJlc3BvbnNlIikpCmBgYAoKYGBge3J9Cm1vcmFuLnRlc3QoYW5hbHlzaXNfZGF0YSRod2lfZmRpdl9ub3JtYWwsIG5iMmxpc3R3KGFuYWx5c2lzX2RhdGFfbm1kc19uZWlnaGJvdXJzKSkKYGBgCgojIyBGRGl2IE1hc3MKCiMjIyBMYXQvTG9uZwpgYGB7cn0Kd2l0aF9yZWFsbXNfbGF0bG9uZyhnZ3Bsb3QoYW5hbHlzaXNfZGF0YSwgYWVzKHggPSBsYXRpdHVkZSwgeSA9IGxvbmdpdHVkZSwgY29sb3VyID0gbWFzc19mZGl2X3N0YW5kYXJkKSkgKyBnZW9tX3BvaW50KCkgKyBzdGFuZGFyZGlzZWRfY29sb3Vyc19zY2FsZSArIGxhYnMoY29sb3VyID0gIlN0YW5kYXJkaXNlZCByZXNwb25zZSIpKQpgYGAKCmBgYHtyfQptb3Jhbi50ZXN0KGFuYWx5c2lzX2RhdGEkbWFzc19mZGl2X3N0YW5kYXJkLCBuYjJsaXN0dyhhbmFseXNpc19kYXRhX25laWdoYm91cnMpKQpgYGAKCmBgYHtyfQp3aXRoX3JlYWxtc19sYXRsb25nKGdncGxvdChhbmFseXNpc19kYXRhLCBhZXMoeCA9IGxhdGl0dWRlLCB5ID0gbG9uZ2l0dWRlLCBjb2xvdXIgPSBtYXNzX2ZkaXZfbm9ybWFsKSkgKyBnZW9tX3BvaW50KCkgKyBzdGFuZGFyZGlzZWRfY29sb3Vyc19zY2FsZSArIGxhYnMoY29sb3VyID0gIk5vcm1hbGlzZWQgcmVzcG9uc2UiKSkKYGBgCgpgYGB7cn0KbW9yYW4udGVzdChhbmFseXNpc19kYXRhJG1hc3NfZmRpdl9ub3JtYWwsIG5iMmxpc3R3KGFuYWx5c2lzX2RhdGFfbmVpZ2hib3VycykpCmBgYAojIyMgTk1EUwpgYGB7cn0Kd2l0aF9yZWFsbXNfbm1kcyhnZ3Bsb3QoYW5hbHlzaXNfZGF0YSwgYWVzKHggPSBOTURTMSwgeSA9IE5NRFMyLCBjb2xvdXIgPSBtYXNzX2ZkaXZfc3RhbmRhcmQpKSArIGdlb21fcG9pbnQoKSArIHN0YW5kYXJkaXNlZF9jb2xvdXJzX3NjYWxlICsgbGFicyhjb2xvdXIgPSAiU3RhbmRhcmRpc2VkIHJlc3BvbnNlIikpCmBgYAoKYGBge3J9Cm1vcmFuLnRlc3QoYW5hbHlzaXNfZGF0YSRtYXNzX2ZkaXZfc3RhbmRhcmQsIG5iMmxpc3R3KGFuYWx5c2lzX2RhdGFfbm1kc19uZWlnaGJvdXJzKSkKYGBgCgpgYGB7cn0Kd2l0aF9yZWFsbXNfbm1kcyhnZ3Bsb3QoYW5hbHlzaXNfZGF0YSwgYWVzKHggPSBOTURTMSwgeSA9IE5NRFMyLCBjb2xvdXIgPSBtYXNzX2ZkaXZfbm9ybWFsKSkgKyBnZW9tX3BvaW50KCkgKyBzdGFuZGFyZGlzZWRfY29sb3Vyc19zY2FsZSArIGxhYnMoY29sb3VyID0gIk5vcm1hbGlzZWQgcmVzcG9uc2UiKSkKYGBgCgpgYGB7cn0KbW9yYW4udGVzdChhbmFseXNpc19kYXRhJG1hc3NfZmRpdl9ub3JtYWwsIG5iMmxpc3R3KGFuYWx5c2lzX2RhdGFfbm1kc19uZWlnaGJvdXJzKSkKYGBgCgoKIyBFeGFtaW5lIGluZGl2aWR1YWwgbWV0cmljcwpgYGB7cn0KYWxsX2V4cGxhbmF0b3JpZXMgPSBjKAogICAgJ2NpdHlfYXZnX25kdmknLCAnY2l0eV9hdmdfZWxldmF0aW9uJywgJ2NpdHlfYXZnX3RlbXAnLAogICAgJ3JlZ2lvbl81MGttX2F2Z19zb2lsX21vaXN0dXJlJywKICAgICdjb3JlX3JlYWxtQWZyb3Ryb3BpYycsICdjb3JlX3JlYWxtQXVzdHJhbGFzaWEnLCAnY29yZV9yZWFsbUluZG9tYWxheWFuJywgJ2NvcmVfcmVhbG1OZWFyY3RpYycsICdjb3JlX3JlYWxtTmVvdHJvcGljJywgJ2NvcmVfcmVhbG1QYWxlYXJjdGljJywKICAgICdoYXNfaW50cm9kdWNlZF9zcGVjaWVzTm8gaW50cm9kdWNlZCBzcGVjaWVzJywgJ2hhc19pbnRyb2R1Y2VkX3NwZWNpZXNJbnRyb2R1Y2VkIHNwZWNpZXMnLAogICAgJ2NvcmVfcmVhbG1BZnJvdHJvcGljOmhhc19pbnRyb2R1Y2VkX3NwZWNpZXNJbnRyb2R1Y2VkIHNwZWNpZXMnLAogICAgJ2NvcmVfcmVhbG1BdXN0cmFsYXNpYTpoYXNfaW50cm9kdWNlZF9zcGVjaWVzSW50cm9kdWNlZCBzcGVjaWVzJywKICAgICdjb3JlX3JlYWxtSW5kb21hbGF5YW46aGFzX2ludHJvZHVjZWRfc3BlY2llc0ludHJvZHVjZWQgc3BlY2llcycsCiAgICAnY29yZV9yZWFsbU5lYXJjdGljOmhhc19pbnRyb2R1Y2VkX3NwZWNpZXNJbnRyb2R1Y2VkIHNwZWNpZXMnLAogICAgJ2NvcmVfcmVhbG1OZW90cm9waWM6aGFzX2ludHJvZHVjZWRfc3BlY2llc0ludHJvZHVjZWQgc3BlY2llcycsCiAgICAnY29yZV9yZWFsbVBhbGVhcmN0aWM6aGFzX2ludHJvZHVjZWRfc3BlY2llc0ludHJvZHVjZWQgc3BlY2llcycsCiAgICAnY29yZV9yZWFsbU9jZWFuaWE6aGFzX2ludHJvZHVjZWRfc3BlY2llc0ludHJvZHVjZWQgc3BlY2llcycKKQoKYWxsX2V4cGxhbmF0b3J5X25hbWVzID0gZmFjdG9yKAogICBjKAogICAgJ0F2Zy4gTkRWSScsICdBdmcuIEVsZXZhdGlvbicsICdBdmcuIFRlbXAuJywKICAgICdBdmcuIFNvaWwgTW9pc3R1cmUnLAogICAgJ0Fmcm90cm9waWMnLCAnQXVzdHJhbGFzaWEnLCAnSW5kb21hbGF5YW4nLCAnTmVhcmN0aWMnLCAnTmVvdHJvcGljJywgJ1BhbGVhcmN0aWMnLAogICAgJ0ludHJvZHVjZWQgQWJzZW50JywgJ0ludHJvZHVjZWQgUHJlc2VudCcsCiAgICAnQWZyb3Ryb3BpY2FsICogSW50cm9kdWNlZCBwcmVzZW50JywKICAgICdBdXN0YWxpYXNpYW4gKiBJbnRyb2R1Y2VkIHByZXNlbnQnLAogICAgJ0luZG9tYWxheWFuICogSW50cm9kdWNlZCBwcmVzZW50JywKICAgICdOZWFyY3RpYyAqIEludHJvZHVjZWQgcHJlc2VudCcsCiAgICAnTmVvdHJvcGljYWwgKiBJbnRyb2R1Y2VkIHByZXNlbnQnLAogICAgJ1BhbGVhcmN0aWMgKiBJbnRyb2R1Y2VkIHByZXNlbnQnLAogICAgJ09jZWFuaWNhbCAqIEludHJvZHVjZWQgcHJlc2VudCcKICApLCBvcmRlcmVkID0gVAopCgpleHBsYW5hdG9yeV9kaWN0aW9uYXJ5ID0gZGF0YS5mcmFtZShleHBsYW5hdG9yeSA9IGFsbF9leHBsYW5hdG9yaWVzLCBleHBsYW5hdG9yeV9uYW1lID0gYWxsX2V4cGxhbmF0b3J5X25hbWVzKQogIAp3aXRoX2V4cGxhbmF0b3J5X3R5cGVfbGFiZWxzID0gZnVuY3Rpb24ocCkgewogIHAgPSBwW3AkZXhwbGFuYXRvcnkgIT0gJyhJbnRlcmNlcHQpJyxdCiAgZXhwbGFuYXRvcnlfbGV2ZWxzID0gYWxsX2V4cGxhbmF0b3JpZXNbYWxsX2V4cGxhbmF0b3JpZXMgJWluJSBwJGV4cGxhbmF0b3J5XQogIHAkZXhwbGFuYXRvcnkgPC0gZmFjdG9yKHAkZXhwbGFuYXRvcnksIGxldmVscyA9IGV4cGxhbmF0b3J5X2xldmVscykKICAKICBwJHR5cGUgPC0gJ1JlYWxtJwogIHAkdHlwZVtwJGV4cGxhbmF0b3J5ICVpbiUgYygnY2l0eV9hdmdfbmR2aScsICdjaXR5X2F2Z19lbGV2YXRpb24nLCAnY2l0eV9hdmdfdGVtcCcpXSA8LSAnQ2l0eSBnZW9ncmFwaHknCiAgcCR0eXBlW3AkZXhwbGFuYXRvcnkgJWluJSBjKCdyZWdpb25fNTBrbV9hdmdfc29pbF9tb2lzdHVyZScpXSA8LSAnUmVnaW9uYWwgKDUwIGttKSBnZW9ncmFwaHknCiAgcCR0eXBlW3AkZXhwbGFuYXRvcnkgJWluJSBjKCdoYXNfaW50cm9kdWNlZF9zcGVjaWVzTm8gaW50cm9kdWNlZCBzcGVjaWVzJywgJ2hhc19pbnRyb2R1Y2VkX3NwZWNpZXNJbnRyb2R1Y2VkIHNwZWNpZXMnKV0gPC0gJ0ludHJvZHVjZWQgc3BlY2llcycKICBwCn0KCndpdGhfZXhwbGFuYXRvcnlfbmFtZXMgPSBmdW5jdGlvbihwKSB7CiAgcCAlPiUgbGVmdF9qb2luKGV4cGxhbmF0b3J5X2RpY3Rpb25hcnkpICU+JSBhcnJhbmdlKGRlc2MoZXhwbGFuYXRvcnlfbmFtZSkpCn0KCnR5cGVfbGFiZWxzID0gZnVuY3Rpb24ocCkgewogIGV4cGxhbmF0b3J5X2xldmVscyA9IGFsbF9leHBsYW5hdG9yaWVzW2FsbF9leHBsYW5hdG9yaWVzICVpbiUgcCRleHBsYW5hdG9yeV0KICBwJGV4cGxhbmF0b3J5IDwtIGZhY3RvcihwJGV4cGxhbmF0b3J5LCBsZXZlbHMgPSBleHBsYW5hdG9yeV9sZXZlbHMpCiAgCiAgcCR0eXBlIDwtICdSZWFsbScKICBwJHR5cGVbcCRleHBsYW5hdG9yeSAlaW4lIGMoJ2NpdHlfYXZnX25kdmknLCAnY2l0eV9hdmdfZWxldmF0aW9uJywgJ2NpdHlfYXZnX3RlbXAnLCAnY2l0eV9hdmdfbWluX21vbnRobHlfdGVtcCcsICdjaXR5X2F2Z19tYXhfbW9udGhseV90ZW1wJywgCiAgICAnY2l0eV9hdmdfbW9udGhseV90ZW1wJywgJ2NpdHlfYXZnX3JhaW5mYWxsJywgJ2NpdHlfYXZnX21heF9tb250aGx5X3JhaW5mYWxsJywgJ2NpdHlfYXZnX21pbl9tb250aGx5X3JhaW5mYWxsJywgCiAgICAnY2l0eV9hdmdfc29pbF9tb2lzdHVyZScsICdjaXR5X21heF9lbGV2JywgJ2NpdHlfbWluX2VsZXYnLCAnY2l0eV9lbGV2X3JhbmdlJyldIDwtICdDaXR5IGdlb2dyYXBoeScKICBwJHR5cGVbcCRleHBsYW5hdG9yeSAlaW4lIGMoJ3JlZ2lvbl81MGttX2F2Z19uZHZpJywgJ3JlZ2lvbl81MGttX2F2Z19lbGV2YXRpb24nLCAncmVnaW9uXzUwa21fYXZnX3NvaWxfbW9pc3R1cmUnLCAncmVnaW9uXzUwa21fbWF4X2VsZXYnLCAKICAgICdyZWdpb25fNTBrbV9taW5fZWxldicsICdyZWdpb25fNTBrbV9lbGV2X3JhbmdlJyldIDwtICdSZWdpb25hbCAoNTAga20pIGdlb2dyYXBoeScKICAgcCR0eXBlW3AkZXhwbGFuYXRvcnkgJWluJSBjKCdyZWdpb25fMjBrbV9hdmdfbmR2aScsICdyZWdpb25fMjBrbV9hdmdfZWxldmF0aW9uJywgJ3JlZ2lvbl8yMGttX2F2Z19zb2lsX21vaXN0dXJlJywgJ3JlZ2lvbl8yMGttX21heF9lbGV2JywgCiAgICAncmVnaW9uXzIwa21fbWluX2VsZXYnLCAncmVnaW9uXzIwa21fZWxldl9yYW5nZScpXSA8LSAnUmVnaW9uYWwgKDIwIGttKSBnZW9ncmFwaHknCiAgcCR0eXBlW3AkZXhwbGFuYXRvcnkgJWluJSBjKCdoYXNfaW50cm9kdWNlZF9zcGVjaWVzTm8gaW50cm9kdWNlZCBzcGVjaWVzJywgJ2hhc19pbnRyb2R1Y2VkX3NwZWNpZXNJbnRyb2R1Y2VkIHNwZWNpZXMnKV0gPC0gJ0ludHJvZHVjZWQgc3BlY2llcycKICBwCn0KYGBgCgpgYGB7cn0KZXhwbGFuYXRvcnlfbGFiZWxzID0gYygKICAnaGFzX2ludHJvZHVjZWRfc3BlY2llcyc9J0ludHJvZHVjZWQgc3BlY2llcycsIAogICdoYXNfaW50cm9kdWNlZF9zcGVjaWVzTm8gaW50cm9kdWNlZCBzcGVjaWVzJz0nSW50cm9kdWNlZCBhYnNlbnQnLCAKICAnaGFzX2ludHJvZHVjZWRfc3BlY2llc0ludHJvZHVjZWQgc3BlY2llcyc9J0ludHJvZHVjZWQgcHJlc2VudCcsCiAgJ2NpdHlfYXZnX25kdmknPSdBdmVyYWdlIE5EVkknLCAKICAnY2l0eV9hdmdfZWxldmF0aW9uJz0nQXZlcmFnZSBlbGV2YXRpb24nLCAKICAnY2l0eV9hdmdfdGVtcCc9J0F2ZXJhZ2UgdGVtcGVyYXR1cmUnLCAKICAnY2l0eV9hdmdfbWluX21vbnRobHlfdGVtcCc9J0F2ZXJhZ2UgbWluaW11bSBtb250aGx5IHRlbXBlcmF0dXJlJywgCiAgJ2NpdHlfYXZnX21heF9tb250aGx5X3RlbXAnPSdBdmVyYWdlIG1heGltdW0gbW9udGhseSB0ZW1wZXJhdHVyZScsIAogICdjaXR5X2F2Z19tb250aGx5X3RlbXAnPSdBdmVyYWdlIG1vbnRobHkgdGVtcGVyYXR1cmUnLCAKICAnY2l0eV9hdmdfcmFpbmZhbGwnPSdBdmVyYWdlIHJhaW5mYWxsJywgCiAgJ2NpdHlfYXZnX21heF9tb250aGx5X3JhaW5mYWxsJz0nQXZlcmFnZSBtYXhpbXVtIG1vbnRobHkgcmFpbmZhbGwnLCAKICAnY2l0eV9hdmdfbWluX21vbnRobHlfcmFpbmZhbGwnPSdBdmVyYWdlIG1pbmltdW0gbW9udGhseSByYWluZmFsbCcsIAogICdjaXR5X2F2Z19zb2lsX21vaXN0dXJlJz0nQXZlcmFnZSBzb2lsIG1vaXN0dXJlJywgCiAgJ2NpdHlfbWF4X2VsZXYnPSdNYXhpbXVtIGVsZXZhdGlvbicsIAogICdjaXR5X21pbl9lbGV2Jz0nTWluaW11bSBlbGV2YXRpb24nLCAKICAnY2l0eV9lbGV2X3JhbmdlJz0nRWxldmF0aW9uIHJhbmdlJywgCiAgJ3JlZ2lvbl8yMGttX2F2Z19uZHZpJz0nQXZlcmFnZSBORFZJJywgCiAgJ3JlZ2lvbl8yMGttX2F2Z19lbGV2YXRpb24nPSdBdmVyYWdlIGVsZXZhdGlvbicsIAogICdyZWdpb25fMjBrbV9hdmdfc29pbF9tb2lzdHVyZSc9J0F2ZXJhZ2Ugc29pbCBtb2lzdHVyZScsIAogICdyZWdpb25fMjBrbV9tYXhfZWxldic9J01heGltdW0gZWxldmF0aW9uJywgCiAgJ3JlZ2lvbl8yMGttX21pbl9lbGV2Jz0nTWluaW11bSBlbGV2YXRpb24nLAogICdyZWdpb25fMjBrbV9lbGV2X3JhbmdlJz0nRWxldmF0aW9uIHJhbmdlJywKICAncmVnaW9uXzUwa21fYXZnX25kdmknPSdBdmVyYWdlIE5EVkknLAogICdyZWdpb25fNTBrbV9hdmdfZWxldmF0aW9uJz0nQXZlcmFnZSBlbGV2YXRpb24nLAogICdyZWdpb25fNTBrbV9hdmdfc29pbF9tb2lzdHVyZSc9J0F2ZXJhZ2Ugc29pbCBtb2lzdHVyZScsIAogICdyZWdpb25fNTBrbV9tYXhfZWxldic9J01heGltdW0gZWxldmF0aW9uJywKICAncmVnaW9uXzUwa21fbWluX2VsZXYnPSdNaW5pbXVtIGVsZXZhdGlvbicsIAogICdyZWdpb25fNTBrbV9lbGV2X3JhbmdlJz0nRWxldmF0aW9uIHJhbmdlJywKICAnYWJzX2xhdGl0dWRlJyA9ICdBYnNvbHV0ZSBsYXRpdHVkZScsCiAgJ2xhdGl0dWRlJyA9ICdMYXRpdHVkZScsCiAgJ2xvbmdpdHVkZScgPSAnTG9uZ2l0dWRlJywKICAnY29yZV9yZWFsbUFmcm90cm9waWMnID0gJ0Fmcm90cm9waWNhbCcsIAogICdjb3JlX3JlYWxtQXVzdHJhbGFzaWEnID0gJ0F1c3RhbGlhc2lhbicsIAogICdjb3JlX3JlYWxtSW5kb21hbGF5YW4nID0gJ0luZG9tYWxheWFuJywgCiAgJ2NvcmVfcmVhbG1OZWFyY3RpYycgPSAnTmVhcmN0aWMnLCAKICAnY29yZV9yZWFsbU5lb3Ryb3BpYycgPSAnTmVvdHJvcGljYWwnLAogICdjb3JlX3JlYWxtUGFsZWFyY3RpYycgPSAnUGFsZWFyY3RpYycsCiAgJ2NvcmVfcmVhbG1PY2VhbmlhJyA9ICdPY2VhbmljYWwnLAogICdjb3JlX3JlYWxtQWZyb3Ryb3BpYzpoYXNfaW50cm9kdWNlZF9zcGVjaWVzSW50cm9kdWNlZCBzcGVjaWVzJyA9ICdBZnJvdHJvcGljYWwgKiBJbnRyb2R1Y2VkIHByZXNlbnQnLAogICdjb3JlX3JlYWxtQXVzdHJhbGFzaWE6aGFzX2ludHJvZHVjZWRfc3BlY2llc0ludHJvZHVjZWQgc3BlY2llcycgPSAnQXVzdGFsaWFzaWFuICogSW50cm9kdWNlZCBwcmVzZW50JywKICAnY29yZV9yZWFsbUluZG9tYWxheWFuOmhhc19pbnRyb2R1Y2VkX3NwZWNpZXNJbnRyb2R1Y2VkIHNwZWNpZXMnID0gJ0luZG9tYWxheWFuICogSW50cm9kdWNlZCBwcmVzZW50JywKICAnY29yZV9yZWFsbU5lYXJjdGljOmhhc19pbnRyb2R1Y2VkX3NwZWNpZXNJbnRyb2R1Y2VkIHNwZWNpZXMnID0gJ05lYXJjdGljICogSW50cm9kdWNlZCBwcmVzZW50JywKICAnY29yZV9yZWFsbU5lb3Ryb3BpYzpoYXNfaW50cm9kdWNlZF9zcGVjaWVzSW50cm9kdWNlZCBzcGVjaWVzJyA9ICdOZW90cm9waWNhbCAqIEludHJvZHVjZWQgcHJlc2VudCcsCiAgJ2NvcmVfcmVhbG1QYWxlYXJjdGljOmhhc19pbnRyb2R1Y2VkX3NwZWNpZXNJbnRyb2R1Y2VkIHNwZWNpZXMnID0gJ1BhbGVhcmN0aWMgKiBJbnRyb2R1Y2VkIHByZXNlbnQnLAogICdjb3JlX3JlYWxtT2NlYW5pYTpoYXNfaW50cm9kdWNlZF9zcGVjaWVzSW50cm9kdWNlZCBzcGVjaWVzJyA9ICdPY2VhbmljYWwgKiBJbnRyb2R1Y2VkIHByZXNlbnQnCikKYGBgCgpgYGB7cn0KY3JlYXRlX2Zvcm11bGEgPSBmdW5jdGlvbihyZXNwb25zZV92YXIpIHsKICBhcy5mb3JtdWxhKHBhc3RlKHJlc3BvbnNlX3ZhciwgJ34gY29yZV9yZWFsbSArIGNpdHlfYXZnX25kdmkgKyBjaXR5X2F2Z19lbGV2YXRpb24gKyBjaXR5X2F2Z190ZW1wICsgcmVnaW9uXzUwa21fYXZnX3NvaWxfbW9pc3R1cmUgKyBoYXNfaW50cm9kdWNlZF9zcGVjaWVzJykpCn0KYGBgCgojIyBIZWxwZXIgcGxvdCBmdW5jdGlvbnMKYGBge3J9Cmdlb21fbWFwID0gZnVuY3Rpb24obWFwX3NmLCB0aXRsZSwgc2NhbGUgPSBzdGFuZGFyZGlzZWRfY29sb3Vyc19zY2FsZSwgY29sb3VyX2xhYmVsID0gJ1N0YW5kYXJkaXNlZFxuUmVzcG9uc2UnKSB7CiAgbm9ybV9tbnRkX2FuYWx5c2lzX2dlbyA9IGdncGxvdCgpICsgCiAgICBnZW9tX3NmKGRhdGEgPSB3b3JsZF9tYXAsIGFlcyhnZW9tZXRyeSA9IGdlb21ldHJ5KSkgKwogICAgbWFwX3NmICsKICAgIHNjYWxlICsKICAgIGxhYnMoY29sb3VyID0gY29sb3VyX2xhYmVsKSArCiAgICB0aGVtZV9idygpICsKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0iYm90dG9tIikKfQoKZ2VvbV9tYXBfc3RkID0gZnVuY3Rpb24obWFwX3NmLCB0aXRsZSkgewogIGdlb21fbWFwKG1hcF9zZiwgdGl0bGUpCn0KCmdlb21fbWFwX25ybSA9IGZ1bmN0aW9uKG1hcF9zZiwgdGl0bGUpIHsKICBnZW9tX21hcChtYXBfc2YsIHRpdGxlLCBub3JtYWxpc2VkX2NvbG91cnNfc2NhbGUsICdOb3JtYWxpc2VkXG5SZXNwb25zZScpCn0KYGBgCgojIyBIZWxwZXIgRHJlZGdlIGZ1bmN0aW9ucwpgYGB7cn0KIyBUYWtlbiBmcm9tIE11TUlOIHBhY2thZ2UKIyBodHRwczovL3JkcnIuaW8vY3Jhbi9NdU1Jbi9zcmMvUi9hdmVyYWdpbmcuUgojIGh0dHBzOi8vcmRyci5pby9jcmFuL011TUluL3NyYy9SL21vZGVsLmF2Zy5SCgouY29lZmFyci5hdmcgPC0KICBmdW5jdGlvbihjZmFyciwgd2VpZ2h0LCByZXZpc2VkLnZhciwgZnVsbCwgYWxwaGEpIHsJCiAgICB3ZWlnaHQgPC0gd2VpZ2h0IC8gc3VtKHdlaWdodCkKICAgIG5Db2VmIDwtIGRpbShjZmFycilbM0xdCiAgICBpZihmdWxsKSB7CiAgICAgIG5hcyA8LSBpcy5uYShjZmFyclssIDFMLCBdKSAmIGlzLm5hKGNmYXJyWywgMkwsIF0pCiAgICAgIGNmYXJyWywgMUwsIF1bbmFzXSA8LSBjZmFyclssIDJMLCBdW25hc10gPC0gMAogICAgICAjY2ZhcnJbLCAxTDoyTCwgXVtpcy5uYShjZmFyclssIDFMOjJMLCBdKV0gPC0gMAogICAgICBpZighYWxsKGlzLm5hKGNmYXJyWywgM0wsIF0pKSkKICAgICAgICBjZmFyclsgLDNMLCBdW2lzLm5hKGNmYXJyWyAsIDNMLCBdKV0gPC0gSW5mCiAgICB9CiAgICAKICAgIGF2Z2NvZWYgPC0gYXJyYXkoZGltID0gYyhuQ29lZiwgNUwpLAogICAgICAgICAgICAgICAgICAgICBkaW1uYW1lcyA9IGxpc3QoZGltbmFtZXMoY2ZhcnIpW1szTF1dLCBjKCJFc3RpbWF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlN0ZC4gRXJyb3IiLCAiQWRqdXN0ZWQgU0UiLCAiTG93ZXIgQ0kiLCAiVXBwZXIgQ0kiKSkpCiAgICBmb3IoaSBpbiBzZXFfbGVuKG5Db2VmKSkKICAgICAgYXZnY29lZltpLCBdIDwtIHBhci5hdmcoY2ZhcnJbLCAxTCwgaV0sIGNmYXJyWywgMkwsIGldLCB3ZWlnaHQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRmID0gY2ZhcnJbLCAzTCwgaV0sIGFscGhhID0gYWxwaGEsIHJldmlzZWQudmFyID0gcmV2aXNlZC52YXIpCiAgICAKICAgIGF2Z2NvZWZbaXMubmFuKGF2Z2NvZWYpXSA8LSBOQQogICAgcmV0dXJuKGF2Z2NvZWYpCiAgfQoKLm1ha2Vjb2VmbWF0IDwtIGZ1bmN0aW9uKGNmKSB7CiAgbm8uYXNlIDwtIGFsbChpcy5uYShjZlssIDNMXSkpCiAgeiA8LSBhYnMoY2ZbLCAxTF0gLyBjZlssIGlmKG5vLmFzZSkgMkwgZWxzZSAzTF0pCiAgcHZhbCA8LSAyICogcG5vcm0oeiwgbG93ZXIudGFpbCA9IEZBTFNFKQogIGNiaW5kKGNmWywgaWYobm8uYXNlKSAxTDoyTCBlbHNlIDFMOjNMLCBkcm9wID0gRkFMU0VdLAogICAgICAgIGB6IHZhbHVlYCA9IHosIGBQcig+fHp8KWAgPSB6YXBzbWFsbChwdmFsKSkKfQoKIyBHZW5lcmF0ZSBtb2RlbCBzZWxlY3Rpb25zIHVzaW5nIGxtZXIsIGRyZWRnZSwgYW5kIG1vZGVsLmF2ZwojIGBmb3J1bWxhYCA6IGEgdHdvLXNpZGVkIGxpbmVhciBmb3JtdWxhIG9iamVjdCBkZXNjcmliaW5nIGJvdGggdGhlIGZpeGVkLWVmZmVjdHMgYW5kIHJhbmRvbS1lZmZlY3RzIHBhcnQgb2YgdGhlIG1vZGVsCiMgYGRhdGFgIDogdGhlIGRhdGEgZnJhbWUgY29udGFpbmluZyB0aGUgdmFyaWFibGVzIGZyb20gdGhlIGZvcm11bGEKIyBgYWljX2RlbHRhYCA6IHRoZSBBSUMgZGVsdGEgdG8gdXNlIGZvciBzZWxlY3RpbmcgbW9kZWxzIGluIG1vZGVsIGF2ZXJhZ2UKbW9kZWxfYXZlcmFnZSA8LSBmdW5jdGlvbihmb3JtdWxhLCBkYXRhLCBhaWNfZGVsdGEgPSAyMCkgewogIG1vZGVsIDwtIGxtKAogICAgZm9ybXVsYSwKICAgIGRhdGE9ZGF0YQogICkKICBkcmVkZ2VfcmVzdWx0IDwtIGRyZWRnZShtb2RlbCkKICBzdW1tYXJ5KG1vZGVsLmF2ZyhkcmVkZ2VfcmVzdWx0LCBzdWJzZXQgPSBkZWx0YSA8IGFpY19kZWx0YSkpCn0KCiMgQ3JlYXRlIGEgc3VtbWFyeSBkYXRhIGZyYW1lIGNvbnRhaW5pbmcgdGhlIHNlbGVjdGVkIHZhcmlhYmxlcyBmcm9tIGEgbW9kZWwKIyBgbW9kZWxfc3VtYCA6IFRoZSBtb2RlbCBzdW1tYXJ5IG91dHB1dCBmcm9tIGBtb2RlbF9hdmVyYWdlYAptb2RlbF9zdW1tYXJ5IDwtIGZ1bmN0aW9uKG1vZGVsX3N1bSkgewogIC5jb2x1bW5fbmFtZSA8LSBmdW5jdGlvbihwb3N0Zml4KSB7CiAgICBwb3N0Zml4CiAgfQogIAogICMganVzdCByZXR1cm4gdGhlIGVzdGltYXRlIGFuZCBwIHZhbHVlCiAgd2VpZ2h0IDwtIG1vZGVsX3N1bSRtc1RhYmxlWywgNUxdCiAgCiAgY29lZm1hdC5mdWxsIDwtIGFzLmRhdGEuZnJhbWUoLm1ha2Vjb2VmbWF0KC5jb2VmYXJyLmF2Zyhtb2RlbF9zdW0kY29lZkFycmF5LCB3ZWlnaHQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhdHRyKG1vZGVsX3N1bSwgInJldmlzZWQudmFyIiksIFRSVUUsIDAuMDUpKSkKICAKICBjb2VmbWF0LnN1YnNldCA8LQogICAgYXMuZGF0YS5mcmFtZSgubWFrZWNvZWZtYXQoLmNvZWZhcnIuYXZnKG1vZGVsX3N1bSRjb2VmQXJyYXksIHdlaWdodCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhdHRyKG1vZGVsX3N1bSwgInJldmlzZWQudmFyIiksIEZBTFNFLCAwLjA1KSkpCiAgCiAgCiAgY29lZm1hdC5zdWJzZXQgPC0gY29lZm1hdC5zdWJzZXRbLWMoMSksIGMoMSwgMiwgNSldCiAgbmFtZXMoY29lZm1hdC5zdWJzZXQpIDwtIGMoLmNvbHVtbl9uYW1lKCJlc3RpbWF0ZSIpLCAuY29sdW1uX25hbWUoImVycm9yIiksIC5jb2x1bW5fbmFtZSgicCIpKQogIGNvZWZtYXQuc3Vic2V0IDwtIHRpYmJsZTo6cm93bmFtZXNfdG9fY29sdW1uKGNvZWZtYXQuc3Vic2V0LCAiZXhwbGFuYXRvcnkiKQogIGNvZWZtYXQuc3Vic2V0JG1vZGVsID0gJ3N1YnNldCcKICAKICBjb2VmbWF0LmZ1bGwgPC0gY29lZm1hdC5mdWxsWy1jKDEpLCBjKDEsIDIsIDUpXQogIG5hbWVzKGNvZWZtYXQuZnVsbCkgPC0gYyguY29sdW1uX25hbWUoImVzdGltYXRlIiksIC5jb2x1bW5fbmFtZSgiZXJyb3IiKSwgLmNvbHVtbl9uYW1lKCJwIikpCiAgY29lZm1hdC5mdWxsIDwtIHRpYmJsZTo6cm93bmFtZXNfdG9fY29sdW1uKGNvZWZtYXQuZnVsbCwgImV4cGxhbmF0b3J5IikKICBjb2VmbWF0LmZ1bGwkbW9kZWwgPSAnZnVsbCcKICAKICByYmluZChjb2VmbWF0LmZ1bGwsIGNvZWZtYXQuc3Vic2V0KQp9CmBgYAoKYGBge3J9CnBsb3RfZHJlZGdlX3Jlc3VsdCA9IGZ1bmN0aW9uKHJlc3VsdF90YWJsZSwgbXUgPSAwKSB7CiAgcCA9IHJlc3VsdF90YWJsZVtyZXN1bHRfdGFibGUkbW9kZWwgPT0gJ2Z1bGwnLF0KICBwID0gdHlwZV9sYWJlbHMocCkKCiAgZ2dwbG90KHAsIGFlcyh5ID0gZXhwbGFuYXRvcnksIHggPSBlc3RpbWF0ZSwgY29sb3VyID0gdHlwZSkpICsgCiAgICBnZW9tX2xpbmUoKSArCiAgICBnZW9tX3BvaW50KCkgKwogICAgZ2VvbV9lcnJvcmJhcihhZXMoeG1pbj1lc3RpbWF0ZS1lcnJvciwgeG1heD1lc3RpbWF0ZStlcnJvciksIHdpZHRoPS4yLAogICAgICAgICAgICAgICAgICAgcG9zaXRpb249cG9zaXRpb25fZG9kZ2UoMC4wNSkpICsKICAgIHNjYWxlX3lfZGlzY3JldGUoCiAgICAgIGxpbWl0cyA9IHJldihsZXZlbHMocCRleHBsYW5hdG9yeSkpLCAKICAgICAgbGFiZWxzID0gZXhwbGFuYXRvcnlfbGFiZWxzKSArCiAgICBzY2FsZV9jb2xvdXJfbWFudWFsKAogICAgICB2YWx1ZXMgPSBjKHJlYWxtX2NvbG91ciwgY2l0eV9nZW9ncmFwaHlfY29sb3VyLCByZWdpb25hbF81MGttX2dlb2dyYXBoeV9jb2xvdXIsIHJlZ2lvbmFsXzIwa21fZ2VvZ3JhcGh5X2NvbG91ciwgaW50cm9kdWNlZF9zcGVjaWVzX2NvbG91ciksIAogICAgICBicmVha3MgPSBjKCdSZWFsbScsICdDaXR5IGdlb2dyYXBoeScsICdSZWdpb25hbCAoNTAga20pIGdlb2dyYXBoeScsICdSZWdpb25hbCAoMjAga20pIGdlb2dyYXBoeScsICdJbnRyb2R1Y2VkIHNwZWNpZXMnKSkgKwogICAgdGhlbWVfYncoKSArCiAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9bXUsIGxpbmV0eXBlPSJkb3R0ZWQiKSArCiAgICBndWlkZXMoY29sb3VyPWd1aWRlX2xlZ2VuZCh0aXRsZT0iUHJlZGljdG9yIHR5cGUiKSkgKyB4bGFiKCdEaWZmZXJlbmNlIGluIHJlc3BvbnNlIGZyb20gMFxuaGFiaXRhdCBmaWx0ZXJpbmcgKDwgMCkgYW5kIGNvbXBldGl0aXZlIGludGVyYWN0aW9ucyAoPiAwKVxuwrEgU3RhbmRhcmQgRXJyb3InKSArIHlsYWIoJ1ByZWRpY3RvcicpICsKICAgIHRoZW1lKGxlZ2VuZC5qdXN0aWZpY2F0aW9uID0gInRvcCIpCn0KYGBgCgojIyBHTFMgU3BhdGlhbCBIZWxwZXJzCmBgYHtyfQpnbHNfbWV0aG9kID0gIk1MIgoKc3BhdGlhbF9tb2RlbCA9IGZ1bmN0aW9uKGZvcm11bGEsIGNvcnJlbGF0aW9uKSB7CiAgZ2xzKAogICAgZm9ybXVsYSwgCiAgICBkYXRhID0gYW5hbHlzaXNfZGF0YSwgCiAgICBjb3JyZWxhdGlvbiA9IGNvcnJlbGF0aW9uLCAKICAgIG1ldGhvZCA9IGdsc19tZXRob2QKICApCn0KCnBsb3Rfc3BhdGlhbF9yZXN1bHQgPSBmdW5jdGlvbihtb2RlbF9yZXN1bHQpIHsKICBtb2RlbF9zdW1tYXJ5ID0gc3VtbWFyeShtb2RlbF9yZXN1bHQpCiAgcmVzdWx0X3RhYmxlID0gYXMuZGF0YS5mcmFtZShtb2RlbF9zdW1tYXJ5JHRUYWJsZSkKICByZXN1bHRfdGFibGUkZXhwbGFuYXRvcnkgPSByb3duYW1lcyhyZXN1bHRfdGFibGUpCiAgCiAgcmVzdWx0X3RhYmxlID0gcmVzdWx0X3RhYmxlICU+JSB3aXRoX2V4cGxhbmF0b3J5X3R5cGVfbGFiZWxzKCkgJT4lIHdpdGhfZXhwbGFuYXRvcnlfbmFtZXMoKQogIAogIGdncGxvdDI6OmdncGxvdChyZXN1bHRfdGFibGUsIGdncGxvdDI6OmFlcyh5PWZhY3RvcihleHBsYW5hdG9yeV9uYW1lLCBsZXZlbCA9IGFsbF9leHBsYW5hdG9yeV9uYW1lcywgb3JkZXJlZCA9IFQpLCB4PVZhbHVlLCBjb2xvdXIgPSB0eXBlKSkgKyAKICAgIGdncGxvdDI6Omdlb21fbGluZSgpICsKICAgIGdncGxvdDI6Omdlb21fcG9pbnQoKSArCiAgICBnZ3Bsb3QyOjpnZW9tX2Vycm9yYmFyKGdncGxvdDI6OmFlcyh4bWluPVZhbHVlLVN0ZC5FcnJvciwgeG1heD1WYWx1ZStTdGQuRXJyb3IpLCB3aWR0aD0uMiwKICAgICAgICAgICAgICAgICAgIHBvc2l0aW9uPWdncGxvdDI6OnBvc2l0aW9uX2RvZGdlKDAuMDUpKSArCiAgICBnZ3Bsb3QyOjp0aGVtZV9idygpICsKICAgIGdncGxvdDI6Omdlb21fdmxpbmUoeGludGVyY2VwdD0wLCBsaW5ldHlwZT0iZG90dGVkIikgKwogICAgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLmp1c3RpZmljYXRpb24gPSAidG9wIikgKwogICAgeWxhYignUHJlZGljdG9yJykgKwogICAgZ3VpZGVzKGNvbG91cj1ndWlkZV9sZWdlbmQodGl0bGU9IlByZWRpY3RvciB0eXBlIikpICsgeGxhYignRGlmZmVyZW5jZSBpbiByZXNwb25zZSBmcm9tIDBcbmhhYml0YXQgZmlsdGVyaW5nICg8IDApIGFuZCBjb21wZXRpdGl2ZSBpbnRlcmFjdGlvbnMgKD4gMClcbsKxIFN0YW5kYXJkIEVycm9yJykgKwogICAgc2NhbGVfY29sb3VyX21hbnVhbCgKICAgICAgdmFsdWVzID0gYyhyZWFsbV9jb2xvdXIsIGNpdHlfZ2VvZ3JhcGh5X2NvbG91ciwgcmVnaW9uYWxfNTBrbV9nZW9ncmFwaHlfY29sb3VyLCBpbnRyb2R1Y2VkX3NwZWNpZXNfY29sb3VyKSwgCiAgICAgIGJyZWFrcyA9IGMoJ1JlYWxtJywgJ0NpdHkgZ2VvZ3JhcGh5JywgJ1JlZ2lvbmFsICg1MCBrbSkgZ2VvZ3JhcGh5JywgJ0ludHJvZHVjZWQgc3BlY2llcycpKSArCiAgICBzY2FsZV95X2Rpc2NyZXRlKGxpbWl0cyA9IHJldihhbGxfZXhwbGFuYXRvcnlfbmFtZXNbYWxsX2V4cGxhbmF0b3J5X25hbWVzICVpbiUgcmVzdWx0X3RhYmxlJGV4cGxhbmF0b3J5X25hbWVdKSkKfQpgYGAKCiMjIyBDaG9vc2UgYmVzdCBzcGF0aWFsIGNvcnJlbGF0aW9uIGZ1bmN0aW9uCgojIyMjIE1OVEQKYGBge3J9CkFJQyhzcGF0aWFsX21vZGVsKGNyZWF0ZV9mb3JtdWxhKCdtbnRkX3N0YW5kYXJkJyksIGNvckxpbihmb3JtID0gfiBOTURTMSArIE5NRFMyICsgbGF0aXR1ZGUgKyBsb25naXR1ZGUpKSkKQUlDKHNwYXRpYWxfbW9kZWwoY3JlYXRlX2Zvcm11bGEoJ21udGRfc3RhbmRhcmQnKSwgY29yTGluKGZvcm0gPSB+IGxhdGl0dWRlICsgbG9uZ2l0dWRlKSkpCkFJQyhzcGF0aWFsX21vZGVsKGNyZWF0ZV9mb3JtdWxhKCdtbnRkX3N0YW5kYXJkJyksIGNvckV4cChmb3JtID0gfiBOTURTMSArIE5NRFMyICsgbGF0aXR1ZGUgKyBsb25naXR1ZGUpKSkKQUlDKHNwYXRpYWxfbW9kZWwoY3JlYXRlX2Zvcm11bGEoJ21udGRfc3RhbmRhcmQnKSwgY29yRXhwKGZvcm0gPSB+IGxhdGl0dWRlICsgbG9uZ2l0dWRlKSkpCkFJQyhzcGF0aWFsX21vZGVsKGNyZWF0ZV9mb3JtdWxhKCdtbnRkX3N0YW5kYXJkJyksIGNvckdhdXMoZm9ybSA9IH4gTk1EUzEgKyBOTURTMiArIGxhdGl0dWRlICsgbG9uZ2l0dWRlKSkpCkFJQyhzcGF0aWFsX21vZGVsKGNyZWF0ZV9mb3JtdWxhKCdtbnRkX3N0YW5kYXJkJyksIGNvckdhdXMoZm9ybSA9IH4gbGF0aXR1ZGUgKyBsb25naXR1ZGUpKSkKQUlDKHNwYXRpYWxfbW9kZWwoY3JlYXRlX2Zvcm11bGEoJ21udGRfc3RhbmRhcmQnKSwgY29yUmF0aW8oZm9ybSA9IH4gTk1EUzEgKyBOTURTMiArIGxhdGl0dWRlICsgbG9uZ2l0dWRlKSkpCkFJQyhzcGF0aWFsX21vZGVsKGNyZWF0ZV9mb3JtdWxhKCdtbnRkX3N0YW5kYXJkJyksIGNvclJhdGlvKGZvcm0gPSB+IGxhdGl0dWRlICsgbG9uZ2l0dWRlKSkpCkFJQyhzcGF0aWFsX21vZGVsKGNyZWF0ZV9mb3JtdWxhKCdtbnRkX3N0YW5kYXJkJyksIGNvclNwaGVyKGZvcm0gPSB+IE5NRFMxICsgTk1EUzIgKyBsYXRpdHVkZSArIGxvbmdpdHVkZSkpKQpBSUMoc3BhdGlhbF9tb2RlbChjcmVhdGVfZm9ybXVsYSgnbW50ZF9zdGFuZGFyZCcpLCBjb3JTcGhlcihmb3JtID0gfiBsYXRpdHVkZSArIGxvbmdpdHVkZSkpKQpgYGAKTU5URDogY29yUmF0aW8gd2l0aCBOTURTICsgbGF0L2xvbmcKCmBgYHtyfQpBSUMoc3BhdGlhbF9tb2RlbChjcmVhdGVfZm9ybXVsYSgnbW50ZF9ub3JtYWwnKSwgY29yTGluKGZvcm0gPSB+IE5NRFMxICsgTk1EUzIgKyBsYXRpdHVkZSArIGxvbmdpdHVkZSkpKQojQUlDKHNwYXRpYWxfbW9kZWwoY3JlYXRlX2Zvcm11bGEoJ21udGRfbm9ybWFsJyksIGNvckxpbihmb3JtID0gfiBsYXRpdHVkZSArIGxvbmdpdHVkZSkpKQpBSUMoc3BhdGlhbF9tb2RlbChjcmVhdGVfZm9ybXVsYSgnbW50ZF9ub3JtYWwnKSwgY29yRXhwKGZvcm0gPSB+IE5NRFMxICsgTk1EUzIgKyBsYXRpdHVkZSArIGxvbmdpdHVkZSkpKQpBSUMoc3BhdGlhbF9tb2RlbChjcmVhdGVfZm9ybXVsYSgnbW50ZF9ub3JtYWwnKSwgY29yRXhwKGZvcm0gPSB+IGxhdGl0dWRlICsgbG9uZ2l0dWRlKSkpCkFJQyhzcGF0aWFsX21vZGVsKGNyZWF0ZV9mb3JtdWxhKCdtbnRkX25vcm1hbCcpLCBjb3JHYXVzKGZvcm0gPSB+IE5NRFMxICsgTk1EUzIgKyBsYXRpdHVkZSArIGxvbmdpdHVkZSkpKQpBSUMoc3BhdGlhbF9tb2RlbChjcmVhdGVfZm9ybXVsYSgnbW50ZF9ub3JtYWwnKSwgY29yR2F1cyhmb3JtID0gfiBsYXRpdHVkZSArIGxvbmdpdHVkZSkpKQpBSUMoc3BhdGlhbF9tb2RlbChjcmVhdGVfZm9ybXVsYSgnbW50ZF9ub3JtYWwnKSwgY29yUmF0aW8oZm9ybSA9IH4gTk1EUzEgKyBOTURTMiArIGxhdGl0dWRlICsgbG9uZ2l0dWRlKSkpCkFJQyhzcGF0aWFsX21vZGVsKGNyZWF0ZV9mb3JtdWxhKCdtbnRkX25vcm1hbCcpLCBjb3JSYXRpbyhmb3JtID0gfiBsYXRpdHVkZSArIGxvbmdpdHVkZSkpKQpBSUMoc3BhdGlhbF9tb2RlbChjcmVhdGVfZm9ybXVsYSgnbW50ZF9ub3JtYWwnKSwgY29yU3BoZXIoZm9ybSA9IH4gTk1EUzEgKyBOTURTMiArIGxhdGl0dWRlICsgbG9uZ2l0dWRlKSkpCkFJQyhzcGF0aWFsX21vZGVsKGNyZWF0ZV9mb3JtdWxhKCdtbnRkX25vcm1hbCcpLCBjb3JTcGhlcihmb3JtID0gfiBsYXRpdHVkZSArIGxvbmdpdHVkZSkpKQpgYGAKCiMjIyMgQmVhayBXaWR0aAoKYGBge3J9CkFJQyhzcGF0aWFsX21vZGVsKGNyZWF0ZV9mb3JtdWxhKCdiZWFrX3dpZHRoX2ZkaXZfc3RhbmRhcmQnKSwgY29yTGluKGZvcm0gPSB+IE5NRFMxICsgTk1EUzIgKyBsYXRpdHVkZSArIGxvbmdpdHVkZSkpKQojQUlDKHNwYXRpYWxfbW9kZWwoY3JlYXRlX2Zvcm11bGEoJ2JlYWtfd2lkdGhfZmRpdl9zdGFuZGFyZCcpLCBjb3JMaW4oZm9ybSA9IH4gbGF0aXR1ZGUgKyBsb25naXR1ZGUpKSkKQUlDKHNwYXRpYWxfbW9kZWwoY3JlYXRlX2Zvcm11bGEoJ2JlYWtfd2lkdGhfZmRpdl9zdGFuZGFyZCcpLCBjb3JFeHAoZm9ybSA9IH4gTk1EUzEgKyBOTURTMiArIGxhdGl0dWRlICsgbG9uZ2l0dWRlKSkpCkFJQyhzcGF0aWFsX21vZGVsKGNyZWF0ZV9mb3JtdWxhKCdiZWFrX3dpZHRoX2ZkaXZfc3RhbmRhcmQnKSwgY29yRXhwKGZvcm0gPSB+IGxhdGl0dWRlICsgbG9uZ2l0dWRlKSkpCkFJQyhzcGF0aWFsX21vZGVsKGNyZWF0ZV9mb3JtdWxhKCdiZWFrX3dpZHRoX2ZkaXZfc3RhbmRhcmQnKSwgY29yR2F1cyhmb3JtID0gfiBOTURTMSArIE5NRFMyICsgbGF0aXR1ZGUgKyBsb25naXR1ZGUpKSkKQUlDKHNwYXRpYWxfbW9kZWwoY3JlYXRlX2Zvcm11bGEoJ2JlYWtfd2lkdGhfZmRpdl9zdGFuZGFyZCcpLCBjb3JHYXVzKGZvcm0gPSB+IGxhdGl0dWRlICsgbG9uZ2l0dWRlKSkpCkFJQyhzcGF0aWFsX21vZGVsKGNyZWF0ZV9mb3JtdWxhKCdiZWFrX3dpZHRoX2ZkaXZfc3RhbmRhcmQnKSwgY29yUmF0aW8oZm9ybSA9IH4gTk1EUzEgKyBOTURTMiArIGxhdGl0dWRlICsgbG9uZ2l0dWRlKSkpCkFJQyhzcGF0aWFsX21vZGVsKGNyZWF0ZV9mb3JtdWxhKCdiZWFrX3dpZHRoX2ZkaXZfc3RhbmRhcmQnKSwgY29yUmF0aW8oZm9ybSA9IH4gbGF0aXR1ZGUgKyBsb25naXR1ZGUpKSkKQUlDKHNwYXRpYWxfbW9kZWwoY3JlYXRlX2Zvcm11bGEoJ2JlYWtfd2lkdGhfZmRpdl9zdGFuZGFyZCcpLCBjb3JTcGhlcihmb3JtID0gfiBOTURTMSArIE5NRFMyICsgbGF0aXR1ZGUgKyBsb25naXR1ZGUpKSkKQUlDKHNwYXRpYWxfbW9kZWwoY3JlYXRlX2Zvcm11bGEoJ2JlYWtfd2lkdGhfZmRpdl9zdGFuZGFyZCcpLCBjb3JTcGhlcihmb3JtID0gfiBsYXRpdHVkZSArIGxvbmdpdHVkZSkpKQpgYGAKQmVhayB3aWR0aDogY29yRXhwIHdpdGggTk1EUyArIGxhdC9sb25nCgpgYGB7cn0KQUlDKHNwYXRpYWxfbW9kZWwoY3JlYXRlX2Zvcm11bGEoJ2JlYWtfd2lkdGhfZmRpdl9ub3JtYWwnKSwgY29yTGluKGZvcm0gPSB+IE5NRFMxICsgTk1EUzIgKyBsYXRpdHVkZSArIGxvbmdpdHVkZSkpKQpBSUMoc3BhdGlhbF9tb2RlbChjcmVhdGVfZm9ybXVsYSgnYmVha193aWR0aF9mZGl2X25vcm1hbCcpLCBjb3JMaW4oZm9ybSA9IH4gbGF0aXR1ZGUgKyBsb25naXR1ZGUpKSkKQUlDKHNwYXRpYWxfbW9kZWwoY3JlYXRlX2Zvcm11bGEoJ2JlYWtfd2lkdGhfZmRpdl9ub3JtYWwnKSwgY29yRXhwKGZvcm0gPSB+IE5NRFMxICsgTk1EUzIgKyBsYXRpdHVkZSArIGxvbmdpdHVkZSkpKQpBSUMoc3BhdGlhbF9tb2RlbChjcmVhdGVfZm9ybXVsYSgnYmVha193aWR0aF9mZGl2X25vcm1hbCcpLCBjb3JFeHAoZm9ybSA9IH4gbGF0aXR1ZGUgKyBsb25naXR1ZGUpKSkKQUlDKHNwYXRpYWxfbW9kZWwoY3JlYXRlX2Zvcm11bGEoJ2JlYWtfd2lkdGhfZmRpdl9ub3JtYWwnKSwgY29yR2F1cyhmb3JtID0gfiBOTURTMSArIE5NRFMyICsgbGF0aXR1ZGUgKyBsb25naXR1ZGUpKSkKQUlDKHNwYXRpYWxfbW9kZWwoY3JlYXRlX2Zvcm11bGEoJ2JlYWtfd2lkdGhfZmRpdl9ub3JtYWwnKSwgY29yR2F1cyhmb3JtID0gfiBsYXRpdHVkZSArIGxvbmdpdHVkZSkpKQpBSUMoc3BhdGlhbF9tb2RlbChjcmVhdGVfZm9ybXVsYSgnYmVha193aWR0aF9mZGl2X25vcm1hbCcpLCBjb3JSYXRpbyhmb3JtID0gfiBOTURTMSArIE5NRFMyICsgbGF0aXR1ZGUgKyBsb25naXR1ZGUpKSkKQUlDKHNwYXRpYWxfbW9kZWwoY3JlYXRlX2Zvcm11bGEoJ2JlYWtfd2lkdGhfZmRpdl9ub3JtYWwnKSwgY29yUmF0aW8oZm9ybSA9IH4gbGF0aXR1ZGUgKyBsb25naXR1ZGUpKSkKQUlDKHNwYXRpYWxfbW9kZWwoY3JlYXRlX2Zvcm11bGEoJ2JlYWtfd2lkdGhfZmRpdl9ub3JtYWwnKSwgY29yU3BoZXIoZm9ybSA9IH4gTk1EUzEgKyBOTURTMiArIGxhdGl0dWRlICsgbG9uZ2l0dWRlKSkpCkFJQyhzcGF0aWFsX21vZGVsKGNyZWF0ZV9mb3JtdWxhKCdiZWFrX3dpZHRoX2ZkaXZfbm9ybWFsJyksIGNvclNwaGVyKGZvcm0gPSB+IGxhdGl0dWRlICsgbG9uZ2l0dWRlKSkpCmBgYAoKIyMjIyBIV0kKYGBge3J9CkFJQyhzcGF0aWFsX21vZGVsKGNyZWF0ZV9mb3JtdWxhKCdod2lfZmRpdl9zdGFuZGFyZCcpLCBjb3JMaW4oZm9ybSA9IH4gTk1EUzEgKyBOTURTMiArIGxhdGl0dWRlICsgbG9uZ2l0dWRlKSkpCkFJQyhzcGF0aWFsX21vZGVsKGNyZWF0ZV9mb3JtdWxhKCdod2lfZmRpdl9zdGFuZGFyZCcpLCBjb3JMaW4oZm9ybSA9IH4gbGF0aXR1ZGUgKyBsb25naXR1ZGUpKSkKQUlDKHNwYXRpYWxfbW9kZWwoY3JlYXRlX2Zvcm11bGEoJ2h3aV9mZGl2X3N0YW5kYXJkJyksIGNvckV4cChmb3JtID0gfiBOTURTMSArIE5NRFMyICsgbGF0aXR1ZGUgKyBsb25naXR1ZGUpKSkKQUlDKHNwYXRpYWxfbW9kZWwoY3JlYXRlX2Zvcm11bGEoJ2h3aV9mZGl2X3N0YW5kYXJkJyksIGNvckV4cChmb3JtID0gfiBsYXRpdHVkZSArIGxvbmdpdHVkZSkpKQpBSUMoc3BhdGlhbF9tb2RlbChjcmVhdGVfZm9ybXVsYSgnaHdpX2ZkaXZfc3RhbmRhcmQnKSwgY29yR2F1cyhmb3JtID0gfiBOTURTMSArIE5NRFMyICsgbGF0aXR1ZGUgKyBsb25naXR1ZGUpKSkKQUlDKHNwYXRpYWxfbW9kZWwoY3JlYXRlX2Zvcm11bGEoJ2h3aV9mZGl2X3N0YW5kYXJkJyksIGNvckdhdXMoZm9ybSA9IH4gbGF0aXR1ZGUgKyBsb25naXR1ZGUpKSkKQUlDKHNwYXRpYWxfbW9kZWwoY3JlYXRlX2Zvcm11bGEoJ2h3aV9mZGl2X3N0YW5kYXJkJyksIGNvclJhdGlvKGZvcm0gPSB+IE5NRFMxICsgTk1EUzIgKyBsYXRpdHVkZSArIGxvbmdpdHVkZSkpKQpBSUMoc3BhdGlhbF9tb2RlbChjcmVhdGVfZm9ybXVsYSgnaHdpX2ZkaXZfc3RhbmRhcmQnKSwgY29yUmF0aW8oZm9ybSA9IH4gbGF0aXR1ZGUgKyBsb25naXR1ZGUpKSkKQUlDKHNwYXRpYWxfbW9kZWwoY3JlYXRlX2Zvcm11bGEoJ2h3aV9mZGl2X3N0YW5kYXJkJyksIGNvclNwaGVyKGZvcm0gPSB+IE5NRFMxICsgTk1EUzIgKyBsYXRpdHVkZSArIGxvbmdpdHVkZSkpKQpBSUMoc3BhdGlhbF9tb2RlbChjcmVhdGVfZm9ybXVsYSgnaHdpX2ZkaXZfc3RhbmRhcmQnKSwgY29yU3BoZXIoZm9ybSA9IH4gbGF0aXR1ZGUgKyBsb25naXR1ZGUpKSkKYGBgCgpIV0k6IGNvckV4cCB3aXRoIE5NRFMgKyBsYXQvbG9uZwoKYGBge3J9CiNBSUMoc3BhdGlhbF9tb2RlbChjcmVhdGVfZm9ybXVsYSgnaHdpX2ZkaXZfbm9ybWFsJyksIGNvckxpbihmb3JtID0gfiBOTURTMSArIE5NRFMyICsgbGF0aXR1ZGUgKyBsb25naXR1ZGUpKSkKI0FJQyhzcGF0aWFsX21vZGVsKGNyZWF0ZV9mb3JtdWxhKCdod2lfZmRpdl9ub3JtYWwnKSwgY29yTGluKGZvcm0gPSB+IGxhdGl0dWRlICsgbG9uZ2l0dWRlKSkpCkFJQyhzcGF0aWFsX21vZGVsKGNyZWF0ZV9mb3JtdWxhKCdod2lfZmRpdl9ub3JtYWwnKSwgY29yRXhwKGZvcm0gPSB+IE5NRFMxICsgTk1EUzIgKyBsYXRpdHVkZSArIGxvbmdpdHVkZSkpKQpBSUMoc3BhdGlhbF9tb2RlbChjcmVhdGVfZm9ybXVsYSgnaHdpX2ZkaXZfbm9ybWFsJyksIGNvckV4cChmb3JtID0gfiBsYXRpdHVkZSArIGxvbmdpdHVkZSkpKQpBSUMoc3BhdGlhbF9tb2RlbChjcmVhdGVfZm9ybXVsYSgnaHdpX2ZkaXZfbm9ybWFsJyksIGNvckdhdXMoZm9ybSA9IH4gTk1EUzEgKyBOTURTMiArIGxhdGl0dWRlICsgbG9uZ2l0dWRlKSkpCkFJQyhzcGF0aWFsX21vZGVsKGNyZWF0ZV9mb3JtdWxhKCdod2lfZmRpdl9ub3JtYWwnKSwgY29yR2F1cyhmb3JtID0gfiBsYXRpdHVkZSArIGxvbmdpdHVkZSkpKQpBSUMoc3BhdGlhbF9tb2RlbChjcmVhdGVfZm9ybXVsYSgnaHdpX2ZkaXZfbm9ybWFsJyksIGNvclJhdGlvKGZvcm0gPSB+IE5NRFMxICsgTk1EUzIgKyBsYXRpdHVkZSArIGxvbmdpdHVkZSkpKQpBSUMoc3BhdGlhbF9tb2RlbChjcmVhdGVfZm9ybXVsYSgnaHdpX2ZkaXZfbm9ybWFsJyksIGNvclJhdGlvKGZvcm0gPSB+IGxhdGl0dWRlICsgbG9uZ2l0dWRlKSkpCkFJQyhzcGF0aWFsX21vZGVsKGNyZWF0ZV9mb3JtdWxhKCdod2lfZmRpdl9ub3JtYWwnKSwgY29yU3BoZXIoZm9ybSA9IH4gTk1EUzEgKyBOTURTMiArIGxhdGl0dWRlICsgbG9uZ2l0dWRlKSkpCkFJQyhzcGF0aWFsX21vZGVsKGNyZWF0ZV9mb3JtdWxhKCdod2lfZmRpdl9ub3JtYWwnKSwgY29yU3BoZXIoZm9ybSA9IH4gbGF0aXR1ZGUgKyBsb25naXR1ZGUpKSkKYGBgCgojIyMjIE1hc3MKYGBge3J9CkFJQyhzcGF0aWFsX21vZGVsKGNyZWF0ZV9mb3JtdWxhKCdtYXNzX2ZkaXZfc3RhbmRhcmQnKSwgY29yTGluKGZvcm0gPSB+IE5NRFMxICsgTk1EUzIgKyBsYXRpdHVkZSArIGxvbmdpdHVkZSkpKQojQUlDKHNwYXRpYWxfbW9kZWwoY3JlYXRlX2Zvcm11bGEoJ21hc3NfZmRpdl9zdGFuZGFyZCcpLCBjb3JMaW4oZm9ybSA9IH4gbGF0aXR1ZGUgKyBsb25naXR1ZGUpKSkKQUlDKHNwYXRpYWxfbW9kZWwoY3JlYXRlX2Zvcm11bGEoJ21hc3NfZmRpdl9zdGFuZGFyZCcpLCBjb3JFeHAoZm9ybSA9IH4gTk1EUzEgKyBOTURTMiArIGxhdGl0dWRlICsgbG9uZ2l0dWRlKSkpCkFJQyhzcGF0aWFsX21vZGVsKGNyZWF0ZV9mb3JtdWxhKCdtYXNzX2ZkaXZfc3RhbmRhcmQnKSwgY29yRXhwKGZvcm0gPSB+IGxhdGl0dWRlICsgbG9uZ2l0dWRlKSkpCkFJQyhzcGF0aWFsX21vZGVsKGNyZWF0ZV9mb3JtdWxhKCdtYXNzX2ZkaXZfc3RhbmRhcmQnKSwgY29yR2F1cyhmb3JtID0gfiBOTURTMSArIE5NRFMyICsgbGF0aXR1ZGUgKyBsb25naXR1ZGUpKSkKQUlDKHNwYXRpYWxfbW9kZWwoY3JlYXRlX2Zvcm11bGEoJ21hc3NfZmRpdl9zdGFuZGFyZCcpLCBjb3JHYXVzKGZvcm0gPSB+IGxhdGl0dWRlICsgbG9uZ2l0dWRlKSkpCkFJQyhzcGF0aWFsX21vZGVsKGNyZWF0ZV9mb3JtdWxhKCdtYXNzX2ZkaXZfc3RhbmRhcmQnKSwgY29yUmF0aW8oZm9ybSA9IH4gTk1EUzEgKyBOTURTMiArIGxhdGl0dWRlICsgbG9uZ2l0dWRlKSkpCkFJQyhzcGF0aWFsX21vZGVsKGNyZWF0ZV9mb3JtdWxhKCdtYXNzX2ZkaXZfc3RhbmRhcmQnKSwgY29yUmF0aW8oZm9ybSA9IH4gbGF0aXR1ZGUgKyBsb25naXR1ZGUpKSkKQUlDKHNwYXRpYWxfbW9kZWwoY3JlYXRlX2Zvcm11bGEoJ21hc3NfZmRpdl9zdGFuZGFyZCcpLCBjb3JTcGhlcihmb3JtID0gfiBOTURTMSArIE5NRFMyICsgbGF0aXR1ZGUgKyBsb25naXR1ZGUpKSkKQUlDKHNwYXRpYWxfbW9kZWwoY3JlYXRlX2Zvcm11bGEoJ21hc3NfZmRpdl9zdGFuZGFyZCcpLCBjb3JTcGhlcihmb3JtID0gfiBsYXRpdHVkZSArIGxvbmdpdHVkZSkpKQpgYGAKCk1hc3M6IGNvckV4cCB3aXRoIE5NRFMgKyBsYXQvbG9uZwoKYGBge3J9CiNBSUMoc3BhdGlhbF9tb2RlbChjcmVhdGVfZm9ybXVsYSgnbWFzc19mZGl2X25vcm1hbCcpLCBjb3JMaW4oZm9ybSA9IH4gTk1EUzEgKyBOTURTMiArIGxhdGl0dWRlICsgbG9uZ2l0dWRlKSkpCiNBSUMoc3BhdGlhbF9tb2RlbChjcmVhdGVfZm9ybXVsYSgnbWFzc19mZGl2X25vcm1hbCcpLCBjb3JMaW4oZm9ybSA9IH4gbGF0aXR1ZGUgKyBsb25naXR1ZGUpKSkKQUlDKHNwYXRpYWxfbW9kZWwoY3JlYXRlX2Zvcm11bGEoJ21hc3NfZmRpdl9ub3JtYWwnKSwgY29yRXhwKGZvcm0gPSB+IE5NRFMxICsgTk1EUzIgKyBsYXRpdHVkZSArIGxvbmdpdHVkZSkpKQpBSUMoc3BhdGlhbF9tb2RlbChjcmVhdGVfZm9ybXVsYSgnbWFzc19mZGl2X25vcm1hbCcpLCBjb3JFeHAoZm9ybSA9IH4gbGF0aXR1ZGUgKyBsb25naXR1ZGUpKSkKQUlDKHNwYXRpYWxfbW9kZWwoY3JlYXRlX2Zvcm11bGEoJ21hc3NfZmRpdl9ub3JtYWwnKSwgY29yR2F1cyhmb3JtID0gfiBOTURTMSArIE5NRFMyICsgbGF0aXR1ZGUgKyBsb25naXR1ZGUpKSkKQUlDKHNwYXRpYWxfbW9kZWwoY3JlYXRlX2Zvcm11bGEoJ21hc3NfZmRpdl9ub3JtYWwnKSwgY29yR2F1cyhmb3JtID0gfiBsYXRpdHVkZSArIGxvbmdpdHVkZSkpKQpBSUMoc3BhdGlhbF9tb2RlbChjcmVhdGVfZm9ybXVsYSgnbWFzc19mZGl2X25vcm1hbCcpLCBjb3JSYXRpbyhmb3JtID0gfiBOTURTMSArIE5NRFMyICsgbGF0aXR1ZGUgKyBsb25naXR1ZGUpKSkKQUlDKHNwYXRpYWxfbW9kZWwoY3JlYXRlX2Zvcm11bGEoJ21hc3NfZmRpdl9ub3JtYWwnKSwgY29yUmF0aW8oZm9ybSA9IH4gbGF0aXR1ZGUgKyBsb25naXR1ZGUpKSkKQUlDKHNwYXRpYWxfbW9kZWwoY3JlYXRlX2Zvcm11bGEoJ21hc3NfZmRpdl9ub3JtYWwnKSwgY29yU3BoZXIoZm9ybSA9IH4gTk1EUzEgKyBOTURTMiArIGxhdGl0dWRlICsgbG9uZ2l0dWRlKSkpCkFJQyhzcGF0aWFsX21vZGVsKGNyZWF0ZV9mb3JtdWxhKCdtYXNzX2ZkaXZfbm9ybWFsJyksIGNvclNwaGVyKGZvcm0gPSB+IGxhdGl0dWRlICsgbG9uZ2l0dWRlKSkpCmBgYAoKYGBge3J9CmNvcnJlbGF0aW9uX2Zvcm11bGEgPSBhcy5mb3JtdWxhKCd+IE5NRFMxICsgTk1EUzIgKyBsYXRpdHVkZSArIGxvbmdpdHVkZScpCgpjb3JyZWxhdGlvbl9mdW5jdGlvbl9mZGl2ID0gZnVuY3Rpb24oKSB7CiAgY29yRXhwKGZvcm0gPSBjb3JyZWxhdGlvbl9mb3JtdWxhKQp9Cgpjb3JyZWxhdGlvbl9mdW5jdGlvbl9tbnRkID0gZnVuY3Rpb24oKSB7CiAgY29yUmF0aW8oZm9ybSA9IGNvcnJlbGF0aW9uX2Zvcm11bGEpCn0KYGBgCgojIyBNTlRECgojIyMgU3RhbmRhcmRpc2VkCmBgYHtyfQpzdGRfbW50ZF9hbmFseXNpc19nZW9fcGxvdCA9IGdlb21fbWFwX3N0ZChnZW9tX3NmKGRhdGEgPSBhbmFseXNpc19kYXRhLCBhZXMoY29sb3IgPSBtbnRkX3N0YW5kYXJkLCBnZW9tZXRyeSA9IGdlb21ldHJ5KSksICdNTlREJykKc3RkX21udGRfYW5hbHlzaXNfZ2VvX3Bsb3QKYGBgCgpgYGB7cn0Kc3RkX21udGRfYW5hbHlzaXNfZGF0YSA9IG1vZGVsX2RhdGEoYW5hbHlzaXNfZGF0YVshaXMubmEoYW5hbHlzaXNfZGF0YSRtbnRkX3N0YW5kYXJkKSxdLCAnbW50ZF9zdGFuZGFyZCcpCnN0ZF9tbnRkX2FuYWx5c2lzX2Zvcm11bGEgPSBjcmVhdGVfZm9ybXVsYSgnbW50ZF9zdGFuZGFyZCcpCnN0ZF9tbnRkX2FuYWx5c2lzX3Jlc3VsdCA9IG1vZGVsX2F2ZXJhZ2Uoc3RkX21udGRfYW5hbHlzaXNfZm9ybXVsYSwgc3RkX21udGRfYW5hbHlzaXNfZGF0YSkKc3RkX21udGRfYW5hbHlzaXNfcmVzdWx0X3RhYmxlID0gbW9kZWxfc3VtbWFyeShzdGRfbW50ZF9hbmFseXNpc19yZXN1bHQpCnN0ZF9tbnRkX2FuYWx5c2lzX3Jlc3VsdF90YWJsZQpgYGAKCmBgYHtyfQpzdGRfbW50ZF9hbmFseXNpc19wcmVkX3Bsb3QgPSBwbG90X2RyZWRnZV9yZXN1bHQoc3RkX21udGRfYW5hbHlzaXNfcmVzdWx0X3RhYmxlKQpzdGRfbW50ZF9hbmFseXNpc19wcmVkX3Bsb3QKYGBgCgpEbyB0aGUgcmVzaWR1YWxzIHN0aWxsIGNvbnRhaW4gc3BhdGlhbCBhdXRvY29ycmVsYXRpb24gZnJvbSBhIGZpdHRlZCBsbT8KYGBge3J9CnN0ZF9tbnRkX2xtID0gbG0oc3RkX21udGRfYW5hbHlzaXNfZm9ybXVsYSwgc3RkX21udGRfYW5hbHlzaXNfZGF0YSkKbW9yYW4udGVzdChzdGRfbW50ZF9sbSRyZXNpZHVhbHMsIG5iMmxpc3R3KGFuYWx5c2lzX2RhdGFfbmVpZ2hib3VycykpCm1vcmFuLnRlc3Qoc3RkX21udGRfbG0kcmVzaWR1YWxzLCBuYjJsaXN0dyhhbmFseXNpc19kYXRhX25tZHNfbmVpZ2hib3VycykpCmBgYAoKYGBge3J9CnN0ZF9tbnRkX3NwYXRpYWxfbW9kZWwgPSBzcGF0aWFsX21vZGVsKHN0ZF9tbnRkX2FuYWx5c2lzX2Zvcm11bGEsIGNvcnJlbGF0aW9uX2Z1bmN0aW9uX21udGQoKSkKbW9yYW4udGVzdChzdGRfbW50ZF9zcGF0aWFsX21vZGVsJHJlc2lkdWFscywgbmIybGlzdHcoYW5hbHlzaXNfZGF0YV9uZWlnaGJvdXJzKSkKbW9yYW4udGVzdChzdGRfbW50ZF9zcGF0aWFsX21vZGVsJHJlc2lkdWFscywgbmIybGlzdHcoYW5hbHlzaXNfZGF0YV9ubWRzX25laWdoYm91cnMpKQpgYGAKCmBgYHtyfQpzdGRfbW50ZF9hbmFseXNpc19wcmVkX3NwYXRpYWxfcGxvdCA9IHBsb3Rfc3BhdGlhbF9yZXN1bHQoc3RkX21udGRfc3BhdGlhbF9tb2RlbCkKc3RkX21udGRfYW5hbHlzaXNfcHJlZF9zcGF0aWFsX3Bsb3QKYGBgCgojIyMgTm9ybWFsaXNlZApgYGB7cn0KbnJtX21udGRfYW5hbHlzaXNfZ2VvX3Bsb3QgPSBnZW9tX21hcF9ucm0oZ2VvbV9zZihkYXRhID0gYW5hbHlzaXNfZGF0YSwgYWVzKGNvbG9yID0gbW50ZF9ub3JtYWwsIGdlb21ldHJ5ID0gZ2VvbWV0cnkpKSwgJ01OVEQnKQpucm1fbW50ZF9hbmFseXNpc19nZW9fcGxvdApgYGAKCmBgYHtyfQpucm1fbW50ZF9hbmFseXNpc19kYXRhID0gbW9kZWxfZGF0YShhbmFseXNpc19kYXRhWyFpcy5uYShhbmFseXNpc19kYXRhJG1udGRfbm9ybWFsKSxdLCAnbW50ZF9ub3JtYWwnKQpucm1fbW50ZF9hbmFseXNpc19mb3JtdWxhID0gY3JlYXRlX2Zvcm11bGEoJ21udGRfbm9ybWFsJykKbnJtX21udGRfYW5hbHlzaXNfcmVzdWx0ID0gbW9kZWxfYXZlcmFnZShucm1fbW50ZF9hbmFseXNpc19mb3JtdWxhLCBucm1fbW50ZF9hbmFseXNpc19kYXRhKQpucm1fbW50ZF9hbmFseXNpc19yZXN1bHRfdGFibGUgPSBtb2RlbF9zdW1tYXJ5KG5ybV9tbnRkX2FuYWx5c2lzX3Jlc3VsdCkKbnJtX21udGRfYW5hbHlzaXNfcmVzdWx0X3RhYmxlCmBgYAoKYGBge3J9Cm5ybV9tbnRkX2FuYWx5c2lzX3ByZWRfcGxvdCA9IHBsb3RfZHJlZGdlX3Jlc3VsdChucm1fbW50ZF9hbmFseXNpc19yZXN1bHRfdGFibGUpCm5ybV9tbnRkX2FuYWx5c2lzX3ByZWRfcGxvdApgYGAKCkRvIHRoZSByZXNpZHVhbHMgc3RpbGwgY29udGFpbiBzcGF0aWFsIGF1dG9jb3JyZWxhdGlvbiBmcm9tIGEgZml0dGVkIGxtPwpgYGB7cn0KbnJtX21udGRfbG0gPSBsbShucm1fbW50ZF9hbmFseXNpc19mb3JtdWxhLCBucm1fbW50ZF9hbmFseXNpc19kYXRhKQptb3Jhbi50ZXN0KG5ybV9tbnRkX2xtJHJlc2lkdWFscywgbmIybGlzdHcoYW5hbHlzaXNfZGF0YV9uZWlnaGJvdXJzKSkKbW9yYW4udGVzdChucm1fbW50ZF9sbSRyZXNpZHVhbHMsIG5iMmxpc3R3KGFuYWx5c2lzX2RhdGFfbm1kc19uZWlnaGJvdXJzKSkKYGBgCgpgYGB7cn0KbnJtX21udGRfc3BhdGlhbF9tb2RlbCA9IHNwYXRpYWxfbW9kZWwobnJtX21udGRfYW5hbHlzaXNfZm9ybXVsYSwgY29ycmVsYXRpb25fZnVuY3Rpb25fbW50ZCgpKQptb3Jhbi50ZXN0KG5ybV9tbnRkX3NwYXRpYWxfbW9kZWwkcmVzaWR1YWxzLCBuYjJsaXN0dyhhbmFseXNpc19kYXRhX25laWdoYm91cnMpKQptb3Jhbi50ZXN0KG5ybV9tbnRkX3NwYXRpYWxfbW9kZWwkcmVzaWR1YWxzLCBuYjJsaXN0dyhhbmFseXNpc19kYXRhX25tZHNfbmVpZ2hib3VycykpCmBgYAoKYGBge3J9Cm5ybV9tbnRkX2FuYWx5c2lzX3ByZWRfc3BhdGlhbF9wbG90ID0gcGxvdF9zcGF0aWFsX3Jlc3VsdChucm1fbW50ZF9zcGF0aWFsX21vZGVsKQpucm1fbW50ZF9hbmFseXNpc19wcmVkX3NwYXRpYWxfcGxvdApgYGAKCiMjIEdhcGUgd2lkdGggLSBGRGl2CiMjIyBTdGFuZGFyZGlzZWQKYGBge3J9CnN0ZF9nYXBlX2ZkaXZfYW5hbHlzaXNfZ2VvX3Bsb3QgPSBnZW9tX21hcF9zdGQoZ2VvbV9zZihkYXRhID0gYW5hbHlzaXNfZGF0YSwgYWVzKGNvbG9yID0gYmVha193aWR0aF9mZGl2X3N0YW5kYXJkLCBnZW9tZXRyeSA9IGdlb21ldHJ5KSksICdCZWFrIFdpZHRoIEZEaXYnKQpzdGRfZ2FwZV9mZGl2X2FuYWx5c2lzX2dlb19wbG90CmBgYAoKCmBgYHtyfQpzdGRfZ2FwZV9mZGl2X2FuYWx5c2lzX2RhdGEgPSBtb2RlbF9kYXRhKGFuYWx5c2lzX2RhdGFbIWlzLm5hKGFuYWx5c2lzX2RhdGEkYmVha193aWR0aF9mZGl2X3N0YW5kYXJkKSxdLCAnYmVha193aWR0aF9mZGl2X3N0YW5kYXJkJykKc3RkX2dhcGVfZmRpdl9hbmFseXNpc19mb3JtdWxhID0gY3JlYXRlX2Zvcm11bGEoJ2JlYWtfd2lkdGhfZmRpdl9zdGFuZGFyZCcpCnN0ZF9nYXBlX2ZkaXZfYW5hbHlzaXNfcmVzdWx0ID0gbW9kZWxfYXZlcmFnZShzdGRfZ2FwZV9mZGl2X2FuYWx5c2lzX2Zvcm11bGEsIHN0ZF9nYXBlX2ZkaXZfYW5hbHlzaXNfZGF0YSkKc3RkX2dhcGVfZmRpdl9hbmFseXNpc19yZXN1bHRfdGFibGUgPSBtb2RlbF9zdW1tYXJ5KHN0ZF9nYXBlX2ZkaXZfYW5hbHlzaXNfcmVzdWx0KQpzdGRfZ2FwZV9mZGl2X2FuYWx5c2lzX3Jlc3VsdF90YWJsZQpgYGAKCmBgYHtyfQpzdGRfZ2FwZV9mZGl2X2FuYWx5c2lzX3ByZWRfcGxvdCA9IHBsb3RfZHJlZGdlX3Jlc3VsdChzdGRfZ2FwZV9mZGl2X2FuYWx5c2lzX3Jlc3VsdF90YWJsZSkKc3RkX2dhcGVfZmRpdl9hbmFseXNpc19wcmVkX3Bsb3QKYGBgCgpEbyB0aGUgcmVzaWR1YWxzIHN0aWxsIGNvbnRhaW4gc3BhdGlhbCBhdXRvY29ycmVsYXRpb24gZnJvbSBhIGZpdHRlZCBsbT8KYGBge3J9CnN0ZF9nYXBlX2ZkaXZfbG0gPSBsbShzdGRfZ2FwZV9mZGl2X2FuYWx5c2lzX2Zvcm11bGEsIHN0ZF9nYXBlX2ZkaXZfYW5hbHlzaXNfZGF0YSkKbW9yYW4udGVzdChzdGRfZ2FwZV9mZGl2X2xtJHJlc2lkdWFscywgbmIybGlzdHcoYW5hbHlzaXNfZGF0YV9uZWlnaGJvdXJzKSkKbW9yYW4udGVzdChzdGRfZ2FwZV9mZGl2X2xtJHJlc2lkdWFscywgbmIybGlzdHcoYW5hbHlzaXNfZGF0YV9ubWRzX25laWdoYm91cnMpKQpgYGAKCmBgYHtyfQpzdGRfZ2FwZV9mZGl2X3NwYXRpYWxfbW9kZWwgPSBzcGF0aWFsX21vZGVsKHN0ZF9nYXBlX2ZkaXZfYW5hbHlzaXNfZm9ybXVsYSwgY29ycmVsYXRpb25fZnVuY3Rpb25fZmRpdigpKQptb3Jhbi50ZXN0KHN0ZF9nYXBlX2ZkaXZfc3BhdGlhbF9tb2RlbCRyZXNpZHVhbHMsIG5iMmxpc3R3KGFuYWx5c2lzX2RhdGFfbmVpZ2hib3VycykpCm1vcmFuLnRlc3Qoc3RkX2dhcGVfZmRpdl9zcGF0aWFsX21vZGVsJHJlc2lkdWFscywgbmIybGlzdHcoYW5hbHlzaXNfZGF0YV9ubWRzX25laWdoYm91cnMpKQpgYGAKCmBgYHtyfQpzdGRfZ2FwZV9mZGl2X2FuYWx5c2lzX3ByZWRfc3BhdGlhbF9wbG90ID0gcGxvdF9zcGF0aWFsX3Jlc3VsdChzdGRfZ2FwZV9mZGl2X3NwYXRpYWxfbW9kZWwpCnN0ZF9nYXBlX2ZkaXZfYW5hbHlzaXNfcHJlZF9zcGF0aWFsX3Bsb3QKYGBgCgojIyMgTm9ybWFsaXNlZApgYGB7cn0KbnJtX2dhcGVfZmRpdl9hbmFseXNpc19nZW9fcGxvdCA9IGdlb21fbWFwX25ybShnZW9tX3NmKGRhdGEgPSBhbmFseXNpc19kYXRhLCBhZXMoY29sb3IgPSBiZWFrX3dpZHRoX2ZkaXZfbm9ybWFsLCBnZW9tZXRyeSA9IGdlb21ldHJ5KSksICdCZWFrIFdpZHRoIEZEaXYnKQpucm1fZ2FwZV9mZGl2X2FuYWx5c2lzX2dlb19wbG90CmBgYAoKCmBgYHtyfQpucm1fZ2FwZV9mZGl2X2FuYWx5c2lzX2RhdGEgPSBtb2RlbF9kYXRhKGFuYWx5c2lzX2RhdGFbIWlzLm5hKGFuYWx5c2lzX2RhdGEkYmVha193aWR0aF9mZGl2X25vcm1hbCksXSwgJ2JlYWtfd2lkdGhfZmRpdl9ub3JtYWwnKQpucm1fZ2FwZV9mZGl2X2FuYWx5c2lzX2Zvcm11bGEgPSBjcmVhdGVfZm9ybXVsYSgnYmVha193aWR0aF9mZGl2X25vcm1hbCcpCm5ybV9nYXBlX2ZkaXZfYW5hbHlzaXNfcmVzdWx0ID0gbW9kZWxfYXZlcmFnZShucm1fZ2FwZV9mZGl2X2FuYWx5c2lzX2Zvcm11bGEsIG5ybV9nYXBlX2ZkaXZfYW5hbHlzaXNfZGF0YSkKbnJtX2dhcGVfZmRpdl9hbmFseXNpc19yZXN1bHRfdGFibGUgPSBtb2RlbF9zdW1tYXJ5KG5ybV9nYXBlX2ZkaXZfYW5hbHlzaXNfcmVzdWx0KQpucm1fZ2FwZV9mZGl2X2FuYWx5c2lzX3Jlc3VsdF90YWJsZQpgYGAKCmBgYHtyfQpucm1fZ2FwZV9mZGl2X2FuYWx5c2lzX3ByZWRfcGxvdCA9IHBsb3RfZHJlZGdlX3Jlc3VsdChucm1fZ2FwZV9mZGl2X2FuYWx5c2lzX3Jlc3VsdF90YWJsZSkKbnJtX2dhcGVfZmRpdl9hbmFseXNpc19wcmVkX3Bsb3QKYGBgCgpEbyB0aGUgcmVzaWR1YWxzIHN0aWxsIGNvbnRhaW4gc3BhdGlhbCBhdXRvY29ycmVsYXRpb24gZnJvbSBhIGZpdHRlZCBsbT8KYGBge3J9Cm5ybV9nYXBlX2ZkaXZfbG0gPSBsbShucm1fZ2FwZV9mZGl2X2FuYWx5c2lzX2Zvcm11bGEsIG5ybV9nYXBlX2ZkaXZfYW5hbHlzaXNfZGF0YSkKbW9yYW4udGVzdChucm1fZ2FwZV9mZGl2X2xtJHJlc2lkdWFscywgbmIybGlzdHcoYW5hbHlzaXNfZGF0YV9uZWlnaGJvdXJzKSkKbW9yYW4udGVzdChucm1fZ2FwZV9mZGl2X2xtJHJlc2lkdWFscywgbmIybGlzdHcoYW5hbHlzaXNfZGF0YV9ubWRzX25laWdoYm91cnMpKQpgYGAKCmBgYHtyfQpucm1fZ2FwZV9mZGl2X3NwYXRpYWxfbW9kZWwgPSBzcGF0aWFsX21vZGVsKG5ybV9nYXBlX2ZkaXZfYW5hbHlzaXNfZm9ybXVsYSwgY29ycmVsYXRpb25fZnVuY3Rpb25fZmRpdigpKQptb3Jhbi50ZXN0KG5ybV9nYXBlX2ZkaXZfc3BhdGlhbF9tb2RlbCRyZXNpZHVhbHMsIG5iMmxpc3R3KGFuYWx5c2lzX2RhdGFfbmVpZ2hib3VycykpCm1vcmFuLnRlc3QobnJtX2dhcGVfZmRpdl9zcGF0aWFsX21vZGVsJHJlc2lkdWFscywgbmIybGlzdHcoYW5hbHlzaXNfZGF0YV9ubWRzX25laWdoYm91cnMpKQpgYGAKCmBgYHtyfQpucm1fZ2FwZV9mZGl2X2FuYWx5c2lzX3ByZWRfc3BhdGlhbF9wbG90ID0gcGxvdF9zcGF0aWFsX3Jlc3VsdChucm1fZ2FwZV9mZGl2X3NwYXRpYWxfbW9kZWwpCm5ybV9nYXBlX2ZkaXZfYW5hbHlzaXNfcHJlZF9zcGF0aWFsX3Bsb3QKYGBgCgojIyBIV0kgLSBGRGl2CiMjIyBTdGFuZGFyZGlzZWQKYGBge3J9CnN0ZF9od2lfZmRpdl9hbmFseXNpc19nZW9fcGxvdCA9IGdlb21fbWFwX3N0ZChnZW9tX3NmKGRhdGEgPSBhbmFseXNpc19kYXRhLCBhZXMoY29sb3IgPSBod2lfZmRpdl9zdGFuZGFyZCwgZ2VvbWV0cnkgPSBnZW9tZXRyeSkpLCAnSFdJIEZEaXYnKQpzdGRfaHdpX2ZkaXZfYW5hbHlzaXNfZ2VvX3Bsb3QKYGBgCgoKCmBgYHtyfQpzdGRfaHdpX2ZkaXZfYW5hbHlzaXNfZGF0YSA9IG1vZGVsX2RhdGEoYW5hbHlzaXNfZGF0YVshaXMubmEoYW5hbHlzaXNfZGF0YSRod2lfZmRpdl9zdGFuZGFyZCksXSwgJ2h3aV9mZGl2X3N0YW5kYXJkJykKc3RkX2h3aV9mZGl2X2FuYWx5c2lzX2Zvcm11bGEgPSBjcmVhdGVfZm9ybXVsYSgnaHdpX2ZkaXZfc3RhbmRhcmQnKQpzdGRfaHdpX2ZkaXZfYW5hbHlzaXNfcmVzdWx0ID0gbW9kZWxfYXZlcmFnZShzdGRfaHdpX2ZkaXZfYW5hbHlzaXNfZm9ybXVsYSwgc3RkX2h3aV9mZGl2X2FuYWx5c2lzX2RhdGEpCnN0ZF9od2lfZmRpdl9hbmFseXNpc19yZXN1bHRfdGFibGUgPSBtb2RlbF9zdW1tYXJ5KHN0ZF9od2lfZmRpdl9hbmFseXNpc19yZXN1bHQpCnN0ZF9od2lfZmRpdl9hbmFseXNpc19yZXN1bHRfdGFibGUKYGBgCgpgYGB7cn0Kc3RkX2h3aV9mZGl2X2FuYWx5c2lzX3ByZWRfcGxvdCA9IHBsb3RfZHJlZGdlX3Jlc3VsdChzdGRfaHdpX2ZkaXZfYW5hbHlzaXNfcmVzdWx0X3RhYmxlKQpzdGRfaHdpX2ZkaXZfYW5hbHlzaXNfcHJlZF9wbG90CmBgYAoKYGBge3J9CnN0ZF9od2lfZmRpdl9sbSA9IGxtKHN0ZF9od2lfZmRpdl9hbmFseXNpc19mb3JtdWxhLCBzdGRfaHdpX2ZkaXZfYW5hbHlzaXNfZGF0YSkKbW9yYW4udGVzdChzdGRfaHdpX2ZkaXZfbG0kcmVzaWR1YWxzLCBuYjJsaXN0dyhhbmFseXNpc19kYXRhX25laWdoYm91cnMpKQptb3Jhbi50ZXN0KHN0ZF9od2lfZmRpdl9sbSRyZXNpZHVhbHMsIG5iMmxpc3R3KGFuYWx5c2lzX2RhdGFfbm1kc19uZWlnaGJvdXJzKSkKYGBgCgpgYGB7cn0Kc3RkX2h3aV9mZGl2X3NwYXRpYWxfbW9kZWwgPSBzcGF0aWFsX21vZGVsKHN0ZF9od2lfZmRpdl9hbmFseXNpc19mb3JtdWxhLCBjb3JyZWxhdGlvbl9mdW5jdGlvbl9mZGl2KCkpCm1vcmFuLnRlc3Qoc3RkX2h3aV9mZGl2X3NwYXRpYWxfbW9kZWwkcmVzaWR1YWxzLCBuYjJsaXN0dyhhbmFseXNpc19kYXRhX25laWdoYm91cnMpKQptb3Jhbi50ZXN0KHN0ZF9od2lfZmRpdl9zcGF0aWFsX21vZGVsJHJlc2lkdWFscywgbmIybGlzdHcoYW5hbHlzaXNfZGF0YV9ubWRzX25laWdoYm91cnMpKQpgYGAKCmBgYHtyfQpzdGRfaHdpX2ZkaXZfYW5hbHlzaXNfcHJlZF9zcGF0aWFsX3Bsb3QgPSBwbG90X3NwYXRpYWxfcmVzdWx0KHN0ZF9od2lfZmRpdl9zcGF0aWFsX21vZGVsKQpzdGRfaHdpX2ZkaXZfYW5hbHlzaXNfcHJlZF9zcGF0aWFsX3Bsb3QKYGBgCgojIyMgTm9ybWFsaXNlZApgYGB7cn0KbnJtX2h3aV9mZGl2X2FuYWx5c2lzX2dlb19wbG90ID0gZ2VvbV9tYXBfbnJtKGdlb21fc2YoZGF0YSA9IGFuYWx5c2lzX2RhdGEsIGFlcyhjb2xvciA9IGh3aV9mZGl2X25vcm1hbCwgZ2VvbWV0cnkgPSBnZW9tZXRyeSkpLCAnSFdJIEZEaXYnKQpucm1faHdpX2ZkaXZfYW5hbHlzaXNfZ2VvX3Bsb3QKYGBgCgoKYGBge3J9Cm5ybV9od2lfZmRpdl9hbmFseXNpc19kYXRhID0gbW9kZWxfZGF0YShhbmFseXNpc19kYXRhWyFpcy5uYShhbmFseXNpc19kYXRhJGh3aV9mZGl2X25vcm1hbCksXSwgJ2h3aV9mZGl2X25vcm1hbCcpCm5ybV9od2lfZmRpdl9hbmFseXNpc19mb3JtdWxhID0gY3JlYXRlX2Zvcm11bGEoJ2h3aV9mZGl2X25vcm1hbCcpCm5ybV9od2lfZmRpdl9hbmFseXNpc19yZXN1bHQgPSBtb2RlbF9hdmVyYWdlKG5ybV9od2lfZmRpdl9hbmFseXNpc19mb3JtdWxhLCBucm1faHdpX2ZkaXZfYW5hbHlzaXNfZGF0YSkKbnJtX2h3aV9mZGl2X2FuYWx5c2lzX3Jlc3VsdF90YWJsZSA9IG1vZGVsX3N1bW1hcnkobnJtX2h3aV9mZGl2X2FuYWx5c2lzX3Jlc3VsdCkKbnJtX2h3aV9mZGl2X2FuYWx5c2lzX3Jlc3VsdF90YWJsZQpgYGAKCmBgYHtyfQpucm1faHdpX2ZkaXZfYW5hbHlzaXNfcHJlZF9wbG90ID0gcGxvdF9kcmVkZ2VfcmVzdWx0KG5ybV9od2lfZmRpdl9hbmFseXNpc19yZXN1bHRfdGFibGUpCm5ybV9od2lfZmRpdl9hbmFseXNpc19wcmVkX3Bsb3QKYGBgCgpgYGB7cn0KbnJtX2h3aV9mZGl2X2xtID0gbG0obnJtX2h3aV9mZGl2X2FuYWx5c2lzX2Zvcm11bGEsIG5ybV9od2lfZmRpdl9hbmFseXNpc19kYXRhKQptb3Jhbi50ZXN0KG5ybV9od2lfZmRpdl9sbSRyZXNpZHVhbHMsIG5iMmxpc3R3KGFuYWx5c2lzX2RhdGFfbmVpZ2hib3VycykpCm1vcmFuLnRlc3QobnJtX2h3aV9mZGl2X2xtJHJlc2lkdWFscywgbmIybGlzdHcoYW5hbHlzaXNfZGF0YV9ubWRzX25laWdoYm91cnMpKQpgYGAKCmBgYHtyfQpucm1faHdpX2ZkaXZfc3BhdGlhbF9tb2RlbCA9IHNwYXRpYWxfbW9kZWwobnJtX2h3aV9mZGl2X2FuYWx5c2lzX2Zvcm11bGEsIGNvcnJlbGF0aW9uX2Z1bmN0aW9uX2ZkaXYoKSkKbW9yYW4udGVzdChucm1faHdpX2ZkaXZfc3BhdGlhbF9tb2RlbCRyZXNpZHVhbHMsIG5iMmxpc3R3KGFuYWx5c2lzX2RhdGFfbmVpZ2hib3VycykpCm1vcmFuLnRlc3QobnJtX2h3aV9mZGl2X3NwYXRpYWxfbW9kZWwkcmVzaWR1YWxzLCBuYjJsaXN0dyhhbmFseXNpc19kYXRhX25tZHNfbmVpZ2hib3VycykpCmBgYAoKYGBge3J9Cm5ybV9od2lfZmRpdl9hbmFseXNpc19wcmVkX3NwYXRpYWxfcGxvdCA9IHBsb3Rfc3BhdGlhbF9yZXN1bHQobnJtX2h3aV9mZGl2X3NwYXRpYWxfbW9kZWwpCm5ybV9od2lfZmRpdl9hbmFseXNpc19wcmVkX3NwYXRpYWxfcGxvdApgYGAKCiMjIE1hc3MgLSBGRGl2CiMjIyBTdGFuZGFyZGlzZWQKYGBge3J9CnN0ZF9tYXNzX2ZkaXZfYW5hbHlzaXNfZ2VvX3Bsb3QgPSBnZW9tX21hcF9zdGQoZ2VvbV9zZihkYXRhID0gYW5hbHlzaXNfZGF0YSwgYWVzKGNvbG9yID0gbWFzc19mZGl2X3N0YW5kYXJkLCBnZW9tZXRyeSA9IGdlb21ldHJ5KSksICdNYXNzIEZEaXYnKQpzdGRfbWFzc19mZGl2X2FuYWx5c2lzX2dlb19wbG90CmBgYAoKCmBgYHtyfQpzdGRfbWFzc19mZGl2X2FuYWx5c2lzX2RhdGEgPSBtb2RlbF9kYXRhKGFuYWx5c2lzX2RhdGFbIWlzLm5hKGFuYWx5c2lzX2RhdGEkbWFzc19mZGl2X3N0YW5kYXJkKSxdLCAnbWFzc19mZGl2X3N0YW5kYXJkJykKc3RkX21hc3NfZmRpdl9hbmFseXNpc19mb3JtdWxhID0gY3JlYXRlX2Zvcm11bGEoJ21hc3NfZmRpdl9zdGFuZGFyZCcpCnN0ZF9tYXNzX2ZkaXZfYW5hbHlzaXNfcmVzdWx0IDwtIG1vZGVsX2F2ZXJhZ2Uoc3RkX21hc3NfZmRpdl9hbmFseXNpc19mb3JtdWxhLCBzdGRfbWFzc19mZGl2X2FuYWx5c2lzX2RhdGEpCnN0ZF9tYXNzX2ZkaXZfYW5hbHlzaXNfcmVzdWx0X3RhYmxlID0gbW9kZWxfc3VtbWFyeShzdGRfbWFzc19mZGl2X2FuYWx5c2lzX3Jlc3VsdCkKc3RkX21hc3NfZmRpdl9hbmFseXNpc19yZXN1bHRfdGFibGUKYGBgCgpgYGB7cn0Kc3RkX21hc3NfZmRpdl9hbmFseXNpc19wcmVkX3Bsb3QgPSBwbG90X2RyZWRnZV9yZXN1bHQoc3RkX21hc3NfZmRpdl9hbmFseXNpc19yZXN1bHRfdGFibGUpCnN0ZF9tYXNzX2ZkaXZfYW5hbHlzaXNfcHJlZF9wbG90CmBgYAoKYGBge3J9CnN0ZF9tYXNzX2ZkaXZfbG0gPSBsbShzdGRfbWFzc19mZGl2X2FuYWx5c2lzX2Zvcm11bGEsIHN0ZF9tYXNzX2ZkaXZfYW5hbHlzaXNfZGF0YSkKbW9yYW4udGVzdChzdGRfbWFzc19mZGl2X2xtJHJlc2lkdWFscywgbmIybGlzdHcoYW5hbHlzaXNfZGF0YV9uZWlnaGJvdXJzKSkKbW9yYW4udGVzdChzdGRfbWFzc19mZGl2X2xtJHJlc2lkdWFscywgbmIybGlzdHcoYW5hbHlzaXNfZGF0YV9ubWRzX25laWdoYm91cnMpKQpgYGAKCmBgYHtyfQpzdGRfbWFzc19mZGl2X3NwYXRpYWxfbW9kZWwgPSBzcGF0aWFsX21vZGVsKHN0ZF9tYXNzX2ZkaXZfYW5hbHlzaXNfZm9ybXVsYSwgY29ycmVsYXRpb25fZnVuY3Rpb25fZmRpdigpKQptb3Jhbi50ZXN0KHN0ZF9tYXNzX2ZkaXZfc3BhdGlhbF9tb2RlbCRyZXNpZHVhbHMsIG5iMmxpc3R3KGFuYWx5c2lzX2RhdGFfbmVpZ2hib3VycykpCm1vcmFuLnRlc3Qoc3RkX21hc3NfZmRpdl9zcGF0aWFsX21vZGVsJHJlc2lkdWFscywgbmIybGlzdHcoYW5hbHlzaXNfZGF0YV9ubWRzX25laWdoYm91cnMpKQpgYGAKCmBgYHtyfQpzdGRfbWFzc19mZGl2X2FuYWx5c2lzX3ByZWRfc3BhdGlhbF9wbG90ID0gcGxvdF9zcGF0aWFsX3Jlc3VsdChzdGRfbWFzc19mZGl2X3NwYXRpYWxfbW9kZWwpCnN0ZF9tYXNzX2ZkaXZfYW5hbHlzaXNfcHJlZF9zcGF0aWFsX3Bsb3QKYGBgCgoKIyMjIE5vcm1hbGlzZWQKYGBge3J9Cm5ybV9tYXNzX2ZkaXZfYW5hbHlzaXNfZ2VvX3Bsb3QgPSBnZW9tX21hcF9ucm0oZ2VvbV9zZihkYXRhID0gYW5hbHlzaXNfZGF0YSwgYWVzKGNvbG9yID0gbWFzc19mZGl2X25vcm1hbCwgZ2VvbWV0cnkgPSBnZW9tZXRyeSkpLCAnTWFzcyBGRGl2JykKbnJtX21hc3NfZmRpdl9hbmFseXNpc19nZW9fcGxvdApgYGAKCgpgYGB7cn0KbnJtX21hc3NfZmRpdl9hbmFseXNpc19kYXRhID0gbW9kZWxfZGF0YShhbmFseXNpc19kYXRhWyFpcy5uYShhbmFseXNpc19kYXRhJG1hc3NfZmRpdl9ub3JtYWwpLF0sICdtYXNzX2ZkaXZfbm9ybWFsJykKbnJtX21hc3NfZmRpdl9hbmFseXNpc19mb3JtdWxhID0gY3JlYXRlX2Zvcm11bGEoJ21hc3NfZmRpdl9ub3JtYWwnKQpucm1fbWFzc19mZGl2X2FuYWx5c2lzX3Jlc3VsdCA9IG1vZGVsX2F2ZXJhZ2UobnJtX21hc3NfZmRpdl9hbmFseXNpc19mb3JtdWxhLCBucm1fbWFzc19mZGl2X2FuYWx5c2lzX2RhdGEpCm5ybV9tYXNzX2ZkaXZfYW5hbHlzaXNfcmVzdWx0X3RhYmxlID0gbW9kZWxfc3VtbWFyeShucm1fbWFzc19mZGl2X2FuYWx5c2lzX3Jlc3VsdCkKbnJtX21hc3NfZmRpdl9hbmFseXNpc19yZXN1bHRfdGFibGUKYGBgCgpgYGB7cn0KbnJtX21hc3NfZmRpdl9hbmFseXNpc19wcmVkX3Bsb3QgPSBwbG90X2RyZWRnZV9yZXN1bHQobnJtX21hc3NfZmRpdl9hbmFseXNpc19yZXN1bHRfdGFibGUpCm5ybV9tYXNzX2ZkaXZfYW5hbHlzaXNfcHJlZF9wbG90CmBgYAoKYGBge3J9Cm5ybV9tYXNzX2ZkaXZfbG0gPSBsbShucm1fbWFzc19mZGl2X2FuYWx5c2lzX2Zvcm11bGEsIG5ybV9tYXNzX2ZkaXZfYW5hbHlzaXNfZGF0YSkKbW9yYW4udGVzdChucm1fbWFzc19mZGl2X2xtJHJlc2lkdWFscywgbmIybGlzdHcoYW5hbHlzaXNfZGF0YV9uZWlnaGJvdXJzKSkKbW9yYW4udGVzdChucm1fbWFzc19mZGl2X2xtJHJlc2lkdWFscywgbmIybGlzdHcoYW5hbHlzaXNfZGF0YV9ubWRzX25laWdoYm91cnMpKQpgYGAKCmBgYHtyfQpucm1fbWFzc19mZGl2X3NwYXRpYWxfbW9kZWwgPSBzcGF0aWFsX21vZGVsKG5ybV9tYXNzX2ZkaXZfYW5hbHlzaXNfZm9ybXVsYSwgY29ycmVsYXRpb25fZnVuY3Rpb25fZmRpdigpKQptb3Jhbi50ZXN0KG5ybV9tYXNzX2ZkaXZfc3BhdGlhbF9tb2RlbCRyZXNpZHVhbHMsIG5iMmxpc3R3KGFuYWx5c2lzX2RhdGFfbmVpZ2hib3VycykpCm1vcmFuLnRlc3QobnJtX21hc3NfZmRpdl9zcGF0aWFsX21vZGVsJHJlc2lkdWFscywgbmIybGlzdHcoYW5hbHlzaXNfZGF0YV9ubWRzX25laWdoYm91cnMpKQpgYGAKCmBgYHtyfQpucm1fbWFzc19mZGl2X2FuYWx5c2lzX3ByZWRfc3BhdGlhbF9wbG90ID0gcGxvdF9zcGF0aWFsX3Jlc3VsdChucm1fbWFzc19mZGl2X3NwYXRpYWxfbW9kZWwpCm5ybV9tYXNzX2ZkaXZfYW5hbHlzaXNfcHJlZF9zcGF0aWFsX3Bsb3QKYGBgCgojIENyZWF0ZSBwbG90IG9mIGRpZmZlcmVuY2VzIGluIHByb2Nlc3MgcmVzcG9uc2UKCgpgYGB7cn0KZmlnID0gZnVuY3Rpb24odHJhaXRfbmFtZSwgcmVzcG9uc2VfdHlwZSwgZ2VvX3Bsb3QsIHByZWRpY3Rvcl9wbG90KSB7CiAgZ3JpZC5hcnJhbmdlKAogICAgYXJyYW5nZUdyb2IoZ3JpZDo6dGV4dEdyb2IodHJhaXRfbmFtZSwgeCA9IDAuMDIsIGhqdXN0ID0gMCwgZ3A9Z3Bhcihmb250ZmFjZT0iYm9sZCIpKSksCiAgICBhcnJhbmdlR3JvYihncmlkOjp0ZXh0R3JvYihwYXN0ZSgnQSknLCByZXNwb25zZV90eXBlLCdyZXNwb25zZSBieSBjaXR5JyksIHggPSAwLjAyLCBoanVzdCA9IDAsIGdwPWdwYXIoZm9udGZhY2U9ImJvbGQiKSkpLAogICAgZ2VvX3Bsb3QsCiAgICBhcnJhbmdlR3JvYihncmlkOjp0ZXh0R3JvYihwYXN0ZSgnQiknLCByZXNwb25zZV90eXBlLCAncmVzcG9uc2UgcHJlZGljdG9ycycpLCB4ID0gMC4wMiwgaGp1c3QgPSAwLCBncD1ncGFyKGZvbnRmYWNlPSJib2xkIikpKSwKICAgIHByZWRpY3Rvcl9wbG90ICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ2JvdHRvbScpICsgZ3VpZGVzKGNvbG91ciA9IGd1aWRlX2xlZ2VuZChucm93PTIsIGJ5cm93PVRSVUUpKSwKICAgIGhlaWdodHMgPSBjKDAuMjUsIDAuMjUsIDQsIDAuMjUsIDMpLAogICAgbnJvdyA9IDUKICApCn0KCgpmaWcoJ01OVEQnLCAnU3RhbmRhcmRpc2VkJywgc3RkX21udGRfYW5hbHlzaXNfZ2VvX3Bsb3QsIHN0ZF9tbnRkX2FuYWx5c2lzX3ByZWRfcGxvdCkKYGBgCiMjIE1OVEQKYGBge3J9CmpwZWcoZmlsZW5hbWUoRklHVVJFU19PVVRQVVRfRElSLCAnZmlndXJlMl9zdGRfbW50ZC5qcGcnKSwgd2lkdGggPSAyMDAsIGhlaWdodCA9IDIyMCwgdW5pdHMgPSAnbW0nLCByZXMgPSAzMDApCmdyaWQuYXJyYW5nZSgKICBhcnJhbmdlR3JvYihmaWcoJ01OVEQnLCAnU3RhbmRhcmRpc2VkJywgc3RkX21udGRfYW5hbHlzaXNfZ2VvX3Bsb3QsIHN0ZF9tbnRkX2FuYWx5c2lzX3ByZWRfcGxvdCkpLAogIG5jb2wgPSAxCiAgKQpkZXYub2ZmKCkKYGBgCgoKYGBge3J9CmpwZWcoZmlsZW5hbWUoRklHVVJFU19PVVRQVVRfRElSLCAnZmlndXJlMl9ucm1fbW50ZC5qcGcnKSwgd2lkdGggPSAyMDAsIGhlaWdodCA9IDIyMCwgdW5pdHMgPSAnbW0nLCByZXMgPSAzMDApCmdyaWQuYXJyYW5nZSgKICBhcnJhbmdlR3JvYihmaWcoJ01OVEQnLCAnTm9ybWFsaXNlZCcsIG5ybV9tbnRkX2FuYWx5c2lzX2dlb19wbG90LCBucm1fbW50ZF9hbmFseXNpc19wcmVkX3Bsb3QpKSwKICBuY29sID0gMQogICkKZGV2Lm9mZigpCmBgYAoKIyMgQmVhayBXaWR0aApgYGB7cn0KanBlZyhmaWxlbmFtZShGSUdVUkVTX09VVFBVVF9ESVIsICdmaWd1cmUyX3N0ZF9iZWFrX3dpZHRoLmpwZycpLCB3aWR0aCA9IDIwMCwgaGVpZ2h0ID0gMjIwLCB1bml0cyA9ICdtbScsIHJlcyA9IDMwMCkKZ3JpZC5hcnJhbmdlKAogIGFycmFuZ2VHcm9iKGZpZygnQmVhayBXaWR0aCcsICdTdGFuZGFyZGlzZWQnLCBzdGRfZ2FwZV9mZGl2X2FuYWx5c2lzX2dlb19wbG90LCBzdGRfZ2FwZV9mZGl2X2FuYWx5c2lzX3ByZWRfcGxvdCkpLAogIG5jb2wgPSAxCiAgKQpkZXYub2ZmKCkKYGBgCgoKYGBge3J9CmpwZWcoZmlsZW5hbWUoRklHVVJFU19PVVRQVVRfRElSLCAnZmlndXJlMl9ucm1fYmVha193aWR0aC5qcGcnKSwgd2lkdGggPSAyMDAsIGhlaWdodCA9IDIyMCwgdW5pdHMgPSAnbW0nLCByZXMgPSAzMDApCmdyaWQuYXJyYW5nZSgKICBhcnJhbmdlR3JvYihmaWcoJ0JlYWsgV2lkdGgnLCAnTm9ybWFsaXNlZCcsIG5ybV9nYXBlX2ZkaXZfYW5hbHlzaXNfZ2VvX3Bsb3QsIG5ybV9nYXBlX2ZkaXZfYW5hbHlzaXNfcHJlZF9wbG90KSksCiAgbmNvbCA9IDEKICApCmRldi5vZmYoKQpgYGAKCiMjIEhXSQpgYGB7cn0KanBlZyhmaWxlbmFtZShGSUdVUkVTX09VVFBVVF9ESVIsICdmaWd1cmUyX3N0ZF9od2kuanBnJyksIHdpZHRoID0gMjAwLCBoZWlnaHQgPSAyMjAsIHVuaXRzID0gJ21tJywgcmVzID0gMzAwKQpncmlkLmFycmFuZ2UoCiAgYXJyYW5nZUdyb2IoZmlnKCdIV0kgKEhhbmQtd2luZyBJbmRleCknLCAnU3RhbmRhcmRpc2VkJywgc3RkX2h3aV9mZGl2X2FuYWx5c2lzX2dlb19wbG90LCBzdGRfaHdpX2ZkaXZfYW5hbHlzaXNfcHJlZF9wbG90KSksCiAgbmNvbCA9IDEKICApCmRldi5vZmYoKQpgYGAKCgpgYGB7cn0KanBlZyhmaWxlbmFtZShGSUdVUkVTX09VVFBVVF9ESVIsICdmaWd1cmUyX25ybV9od2kuanBnJyksIHdpZHRoID0gMjAwLCBoZWlnaHQgPSAyMjAsIHVuaXRzID0gJ21tJywgcmVzID0gMzAwKQpncmlkLmFycmFuZ2UoCiAgYXJyYW5nZUdyb2IoZmlnKCdIV0kgKEhhbmQtd2luZyBJbmRleCknLCAnTm9ybWFsaXNlZCcsIG5ybV9od2lfZmRpdl9hbmFseXNpc19nZW9fcGxvdCwgbnJtX2h3aV9mZGl2X2FuYWx5c2lzX3ByZWRfcGxvdCkpLAogIG5jb2wgPSAxCiAgKQpkZXYub2ZmKCkKYGBgCgojIyBNYXNzCmBgYHtyfQpqcGVnKGZpbGVuYW1lKEZJR1VSRVNfT1VUUFVUX0RJUiwgJ2ZpZ3VyZTJfc3RkX21hc3MuanBnJyksIHdpZHRoID0gMjAwLCBoZWlnaHQgPSAyMjAsIHVuaXRzID0gJ21tJywgcmVzID0gMzAwKQpncmlkLmFycmFuZ2UoCiAgYXJyYW5nZUdyb2IoZmlnKCdNYXNzJywgJ1N0YW5kYXJkaXNlZCcsIHN0ZF9tYXNzX2ZkaXZfYW5hbHlzaXNfZ2VvX3Bsb3QsIHN0ZF9tYXNzX2ZkaXZfYW5hbHlzaXNfcHJlZF9wbG90KSksCiAgbmNvbCA9IDEKICApCmRldi5vZmYoKQpgYGAKCgpgYGB7cn0KanBlZyhmaWxlbmFtZShGSUdVUkVTX09VVFBVVF9ESVIsICdmaWd1cmUyX25ybV9tYXNzLmpwZycpLCB3aWR0aCA9IDIwMCwgaGVpZ2h0ID0gMjIwLCB1bml0cyA9ICdtbScsIHJlcyA9IDMwMCkKZ3JpZC5hcnJhbmdlKAogIGFycmFuZ2VHcm9iKGZpZygnTWFzcycsICdOb3JtYWxpc2VkJywgbnJtX21hc3NfZmRpdl9hbmFseXNpc19nZW9fcGxvdCwgbnJtX21hc3NfZmRpdl9hbmFseXNpc19wcmVkX3Bsb3QpKSwKICBuY29sID0gMQogICkKZGV2Lm9mZigpCmBgYAoKIyBDb21wYXJlIHRvIHNwYXRpYWwgbW9kZWwKCmBgYHtyfQpwbG90X2dyaWQoCiAgcGxvdF9ncmlkKAogICAgZ2dkcmF3KCkgKyAKICAgIGRyYXdfbGFiZWwoCiAgICAgICJTcGF0aWFsIE1vZGVsIChzdGFuZGFyZGlzZWQpIiwKICAgICAgZm9udGZhY2UgPSAnYm9sZCcsCiAgICAgIHggPSAwLAogICAgICBoanVzdCA9IDAKICAgICksCiAgICBnZ2RyYXcoKSArIAogICAgZHJhd19sYWJlbCgKICAgICAgIlNwYXRpYWwgTW9kZWwgKG5vcm1hbGlzZWQpIiwKICAgICAgZm9udGZhY2UgPSAnYm9sZCcsCiAgICAgIHggPSAwLAogICAgICBoanVzdCA9IDAKICAgICksCiAgICBnZ2RyYXcoKSArIAogICAgZHJhd19sYWJlbCgKICAgICAgIkRyZWRnZSBSZXN1bHQgKHN0YW5kYXJkaXNlZCkiLAogICAgICBmb250ZmFjZSA9ICdib2xkJywKICAgICAgeCA9IDAsCiAgICAgIGhqdXN0ID0gMAogICAgKSwKICAgIGdnZHJhdygpICsgCiAgICBkcmF3X2xhYmVsKAogICAgICAiRHJlZGdlIFJlc3VsdCAobm9ybWFsaXNlZCkiLAogICAgICBmb250ZmFjZSA9ICdib2xkJywKICAgICAgeCA9IDAsCiAgICAgIGhqdXN0ID0gMAogICAgKSwKICAgIG5yb3cgPSAxCiAgKSwKICBwbG90X2dyaWQoCiAgICBzdGRfbW50ZF9hbmFseXNpc19wcmVkX3NwYXRpYWxfcGxvdCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsgc2NhbGVfeF9jb250aW51b3VzKG5hbWUgPSAnJywgbGltaXRzID0gYygtMywgMykpICsgeWxhYignJyksIAogICAgbnJtX21udGRfYW5hbHlzaXNfcHJlZF9zcGF0aWFsX3Bsb3QgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArIHNjYWxlX3hfY29udGludW91cyhuYW1lID0gJycsIGxpbWl0cyA9IGMoLTEsIDEpKSArIHlsYWIoJycpLCAKICAgIHN0ZF9tbnRkX2FuYWx5c2lzX3ByZWRfcGxvdCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsgc2NhbGVfeF9jb250aW51b3VzKG5hbWUgPSAnJywgbGltaXRzID0gYygtMywgMykpICsgeWxhYignJyksIAogICAgbnJtX21udGRfYW5hbHlzaXNfcHJlZF9wbG90ICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikgKyBzY2FsZV94X2NvbnRpbnVvdXMobmFtZSA9ICcnLCBsaW1pdHMgPSBjKC0xLCAxKSkgKyB5bGFiKCcnKSwgCiAgICBucm93ID0gMQogICkgKyBkcmF3X2xhYmVsKCJNTlREIiwgc2l6ZSA9IDE2LCBhbmdsZSA9IDkwLCB4ID0gMC4wMSwgeSA9IDAuNSksCiAgcGxvdF9ncmlkKAogICAgc3RkX2dhcGVfZmRpdl9hbmFseXNpc19wcmVkX3NwYXRpYWxfcGxvdCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsgc2NhbGVfeF9jb250aW51b3VzKG5hbWUgPSAnJywgbGltaXRzID0gYygtMywgMykpICsgeWxhYignJyksIAogICAgbnJtX2dhcGVfZmRpdl9hbmFseXNpc19wcmVkX3NwYXRpYWxfcGxvdCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsgc2NhbGVfeF9jb250aW51b3VzKG5hbWUgPSAnJywgbGltaXRzID0gYygtMSwgMSkpICsgeWxhYignJyksIAogICAgc3RkX2dhcGVfZmRpdl9hbmFseXNpc19wcmVkX3Bsb3QgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArIHNjYWxlX3hfY29udGludW91cyhuYW1lID0gJycsIGxpbWl0cyA9IGMoLTMsIDMpKSArIHlsYWIoJycpLCAgCiAgICBucm1fZ2FwZV9mZGl2X2FuYWx5c2lzX3ByZWRfcGxvdCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsgc2NhbGVfeF9jb250aW51b3VzKG5hbWUgPSAnJywgbGltaXRzID0gYygtMSwgMSkpICsgeWxhYignJyksIAogICAgbnJvdyA9IDEKICApICsgZHJhd19sYWJlbCgiQmVhayBXaWR0aCIsIHNpemUgPSAxNiwgYW5nbGUgPSA5MCwgeCA9IDAuMDEsIHkgPSAwLjUpLAogIHBsb3RfZ3JpZCgKICAgIHN0ZF9od2lfZmRpdl9hbmFseXNpc19wcmVkX3NwYXRpYWxfcGxvdCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsgc2NhbGVfeF9jb250aW51b3VzKG5hbWUgPSAnJywgbGltaXRzID0gYygtMywgMykpICsgeWxhYignJyksIAogICAgbnJtX2h3aV9mZGl2X2FuYWx5c2lzX3ByZWRfc3BhdGlhbF9wbG90ICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikgKyBzY2FsZV94X2NvbnRpbnVvdXMobmFtZSA9ICcnLCBsaW1pdHMgPSBjKC0xLCAxKSkgKyB5bGFiKCcnKSwgCiAgICBzdGRfaHdpX2ZkaXZfYW5hbHlzaXNfcHJlZF9wbG90ICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikgKyBzY2FsZV94X2NvbnRpbnVvdXMobmFtZSA9ICcnLCBsaW1pdHMgPSBjKC0zLCAzKSkgKyB5bGFiKCcnKSwgIAogICAgbnJtX2h3aV9mZGl2X2FuYWx5c2lzX3ByZWRfcGxvdCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsgc2NhbGVfeF9jb250aW51b3VzKG5hbWUgPSAnJywgbGltaXRzID0gYygtMSwgMSkpICsgeWxhYignJyksIAogICAgbnJvdyA9IDEKICApICsgZHJhd19sYWJlbCgiSFdJIiwgc2l6ZSA9IDE2LCBhbmdsZSA9IDkwLCB4ID0gMC4wMSwgeSA9IDAuNSksCiAgcGxvdF9ncmlkKAogICAgc3RkX21hc3NfZmRpdl9hbmFseXNpc19wcmVkX3NwYXRpYWxfcGxvdCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsgc2NhbGVfeF9jb250aW51b3VzKG5hbWUgPSAnJywgbGltaXRzID0gYygtMywgMykpICsgeWxhYignJyksIAogICAgbnJtX21hc3NfZmRpdl9hbmFseXNpc19wcmVkX3NwYXRpYWxfcGxvdCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsgc2NhbGVfeF9jb250aW51b3VzKG5hbWUgPSAnJywgbGltaXRzID0gYygtMSwgMSkpICsgeWxhYignJyksIAogICAgc3RkX21hc3NfZmRpdl9hbmFseXNpc19wcmVkX3Bsb3QgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArIHNjYWxlX3hfY29udGludW91cyhuYW1lID0gJycsIGxpbWl0cyA9IGMoLTMsIDMpKSArIHlsYWIoJycpLCAgCiAgICBucm1fbWFzc19mZGl2X2FuYWx5c2lzX3ByZWRfcGxvdCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsgc2NhbGVfeF9jb250aW51b3VzKG5hbWUgPSAnJywgbGltaXRzID0gYygtMSwgMSkpICsgeWxhYignJyksIAogICAgbnJvdyA9IDEKICApICsgZHJhd19sYWJlbCgiTWFzcyIsIHNpemUgPSAxNiwgYW5nbGUgPSA5MCwgeCA9IDAuMDEsIHkgPSAwLjUpLCAKICBucm93ID0gNSwgcmVsX2hlaWdodHMgPSBjKDEsIDQsIDQsIDQsIDQpCikKZ2dzYXZlKGZpbGVuYW1lKEZJR1VSRVNfT1VUUFVUX0RJUiwgJ3Byb2Nlc3NfcmVzcG9uc2VfdnNfc3BhdGlhbC5qcGcnKSwgd2lkdGggPSA0MDAwLCBoZWlnaHQgPSAzMjAwLCB1bml0cyA9ICdweCcpCmBgYAoKCgojIENvbXBhcmUgbWV0cmljcyBhZ2FpbnN0IGVhY2ggb3RoZXIKYGBge3J9CmdncGxvdChhbmFseXNpc19kYXRhLCBhZXMoeCA9IGJlYWtfd2lkdGhfZmRpdl9zdGFuZGFyZCwgeSA9IG1udGRfc3RhbmRhcmQsIGNvbG91ciA9IGNvcmVfcmVhbG0pKSArIAogIGdlb21fcG9pbnQoKSArCiAgeWxhYigiTU5URCIpICsgCiAgeGxhYigiQmVhayBXaWR0aCBGRGl2IikgKwogIHRoZW1lX2J3KCkgKyBsYWJzKGNvbG9yID0gIlJlYWxtIikKYGBgCgpgYGB7cn0KZ2dwbG90KGFuYWx5c2lzX2RhdGEsIGFlcyh4ID0gaHdpX2ZkaXZfc3RhbmRhcmQsIHkgPSBtbnRkX3N0YW5kYXJkLCBjb2xvdXIgPSBjb3JlX3JlYWxtKSkgKyAKICBnZW9tX3BvaW50KCkgKwogIHlsYWIoIk1OVEQiKSArIAogIHhsYWIoIkhXSSBGRGl2IikgKwogIHRoZW1lX2J3KCkgKyBsYWJzKGNvbG9yID0gIlJlYWxtIikKYGBgCgpgYGB7cn0KZ2dwbG90KGFuYWx5c2lzX2RhdGEsIGFlcyh4ID0gaHdpX2ZkaXZfc3RhbmRhcmQsIHkgPSBiZWFrX3dpZHRoX2ZkaXZfc3RhbmRhcmQsIGNvbG91ciA9IGNvcmVfcmVhbG0pKSArIAogIGdlb21fcG9pbnQoKSArCiAgeWxhYigiQmVhayBXaWR0aCBGRGl2IikgKyAKICB4bGFiKCJIV0kgRkRpdiIpICsKICB0aGVtZV9idygpICsgbGFicyhjb2xvciA9ICJSZWFsbSIpCmBgYAoKYGBge3J9Cm1udGRfZmRpdl9hbmFseXNpcyA9IGFuYWx5c2lzX2RhdGEgJT4lIAogIGRwbHlyOjpzZWxlY3QoY2l0eV9pZCwgIG1udGRfc3RhbmRhcmQsIGh3aV9mZGl2X3N0YW5kYXJkLCBiZWFrX3dpZHRoX2ZkaXZfc3RhbmRhcmQsIG1hc3NfZmRpdl9zdGFuZGFyZCkgJT4lCiAgbGVmdF9qb2luKGNvbW11bml0eV9zdW1tYXJ5KSAlPiUKICBtdXRhdGUodXJiYW5fcG9vbF9wZXJjID0gdXJiYW5fcG9vbF9zaXplICogMTAwIC8gcmVnaW9uYWxfcG9vbF9zaXplKQptbnRkX2ZkaXZfYW5hbHlzaXMKYGBgCgpgYGB7cn0KZ2dwYWlycyhtbnRkX2ZkaXZfYW5hbHlzaXMgJT4lIGRwbHlyOjpzZWxlY3QobW50ZF9zdGFuZGFyZCwgaHdpX2ZkaXZfc3RhbmRhcmQsIGJlYWtfd2lkdGhfZmRpdl9zdGFuZGFyZCwgbWFzc19mZGl2X3N0YW5kYXJkLCByZWdpb25hbF9wb29sX3NpemUsIHVyYmFuX3Bvb2xfc2l6ZSwgdXJiYW5fcG9vbF9wZXJjKSwgY29sdW1uTGFiZWxzID0gYygnTU5URCcsICdIV0kgRkQnLCAnQmsgRkQnLCAnTXNzIEZEJywgJ1JlZ2lvbiBSaWNoLicsICdVcmJhbiBSaWNoLicsICclIFVyYmFuJykpCmdnc2F2ZShmaWxlbmFtZShGSUdVUkVTX09VVFBVVF9ESVIsICdhcHBlbmRpeF9zdGFuZGFyaXNlZF9jb3JyZWxhdGlvbi5qcGcnKSkKYGBgCgojIEludHJhY3Rpb24gdGVybSBiZXR3ZWVuIGludHJvZHVjZWQgc3BlY2llcyBhbmQgcmVhbG0KCmBgYHtyfQpjcmVhdGVfaW50ZXJhY3Rpb25fZm9ybXVsYSA9IGZ1bmN0aW9uKHJlc3BvbnNlX3ZhcikgewogIGFzLmZvcm11bGEocGFzdGUocmVzcG9uc2VfdmFyLCAnfiBjb3JlX3JlYWxtICogaGFzX2ludHJvZHVjZWRfc3BlY2llcycpKQp9CmBgYAoKYGBge3J9CmludGVyYWN0aW9uX3R5cGVfbGFiZWxzID0gZnVuY3Rpb24ocCkgewogIGV4cGxhbmF0b3J5X2xldmVscyA9IGFsbF9leHBsYW5hdG9yaWVzW2FsbF9leHBsYW5hdG9yaWVzICVpbiUgcCRleHBsYW5hdG9yeV0KICBwJGV4cGxhbmF0b3J5IDwtIGZhY3RvcihwJGV4cGxhbmF0b3J5LCBsZXZlbHMgPSBleHBsYW5hdG9yeV9sZXZlbHMpCiAgCiAgcCR0eXBlIDwtICdJbnRlcmFjdGlvbicKICBwJHR5cGVbcCRleHBsYW5hdG9yeSAlaW4lIGMoJ2NvcmVfcmVhbG1OZWFyY3RpYycsICdjb3JlX3JlYWxtTmVvdHJvcGljJywgJ2NvcmVfcmVhbG1BZnJvdHJvcGljJywgJ2NvcmVfcmVhbG1JbmRvbWFsYXlhbicsICdjb3JlX3JlYWxtQXVzdHJhbGFzaWEnKV0gPC0gJ1JlYWxtJwogIHAkdHlwZVtwJGV4cGxhbmF0b3J5ICVpbiUgYygnaGFzX2ludHJvZHVjZWRfc3BlY2llc05vIGludHJvZHVjZWQgc3BlY2llcycsICdoYXNfaW50cm9kdWNlZF9zcGVjaWVzSW50cm9kdWNlZCBzcGVjaWVzJyldIDwtICdJbnRyb2R1Y2VkIHNwZWNpZXMnCiAgcAp9CmBgYAoKYGBge3J9CnBsb3RfZHJlZGdlX2ludGVyYWN0aW9uX3Jlc3VsdCA9IGZ1bmN0aW9uKHJlc3VsdF90YWJsZSwgbXUgPSAwKSB7CiAgcCA9IHJlc3VsdF90YWJsZVtyZXN1bHRfdGFibGUkbW9kZWwgPT0gJ2Z1bGwnLF0KICBwID0gaW50ZXJhY3Rpb25fdHlwZV9sYWJlbHMocCkKCiAgZ2dwbG90KHAsIGFlcyh5ID0gZXhwbGFuYXRvcnksIHggPSBlc3RpbWF0ZSwgY29sb3VyID0gdHlwZSkpICsgCiAgICBnZW9tX2xpbmUoKSArCiAgICBnZW9tX3BvaW50KCkgKwogICAgZ2VvbV9lcnJvcmJhcihhZXMoeG1pbj1lc3RpbWF0ZS1lcnJvciwgeG1heD1lc3RpbWF0ZStlcnJvciksIHdpZHRoPS4yLAogICAgICAgICAgICAgICAgICAgcG9zaXRpb249cG9zaXRpb25fZG9kZ2UoMC4wNSkpICsKICAgIHNjYWxlX3lfZGlzY3JldGUoCiAgICAgIGxpbWl0cyA9IHJldihsZXZlbHMocCRleHBsYW5hdG9yeSkpLCAKICAgICAgbGFiZWxzID0gZXhwbGFuYXRvcnlfbGFiZWxzKSArCiAgICBzY2FsZV9jb2xvdXJfbWFudWFsKAogICAgICB2YWx1ZXMgPSBjKHJlYWxtX2NvbG91ciwgaW50ZXJhY3Rpb25fY29sb3VyLCBpbnRyb2R1Y2VkX3NwZWNpZXNfY29sb3VyKSwgCiAgICAgIGJyZWFrcyA9IGMoJ1JlYWxtJywgJ0ludGVyYWN0aW9uJywgJ0ludHJvZHVjZWQgc3BlY2llcycpKSArCiAgICB0aGVtZV9idygpICsKICAgIGdlb21fdmxpbmUoeGludGVyY2VwdD1tdSwgbGluZXR5cGU9ImRvdHRlZCIpICsKICAgIGd1aWRlcyhjb2xvdXI9Z3VpZGVfbGVnZW5kKHRpdGxlPSJQcmVkaWN0b3IgdHlwZSIpKSArIHhsYWIoJ0RpZmZlcmVuY2UgaW4gcmVzcG9uc2UgZnJvbSAwXG5oYWJpdGF0IGZpbHRlcmluZyAoPCAwKSBhbmQgY29tcGV0aXRpdmUgaW50ZXJhY3Rpb25zICg+IDApXG7CsSBTdGFuZGFyZCBFcnJvcicpICsgeWxhYignUHJlZGljdG9yJykgKwogICAgdGhlbWUobGVnZW5kLmp1c3RpZmljYXRpb24gPSAidG9wIikKfQpgYGAKCiMjIE1OVEQKCiMjIyBTdGFuZGFyZGlzZWQKYGBge3J9CnN0ZF9tbnRkX2ludGVyYWN0aW9uX2Zvcm11bGEgPSBjcmVhdGVfaW50ZXJhY3Rpb25fZm9ybXVsYSgnbW50ZF9zdGFuZGFyZCcpCnN0ZF9tbnRkX2ludGVyYWN0aW9uX3Jlc3VsdCA9IG1vZGVsX2F2ZXJhZ2Uoc3RkX21udGRfaW50ZXJhY3Rpb25fZm9ybXVsYSwgc3RkX21udGRfYW5hbHlzaXNfZGF0YSkKc3RkX21udGRfaW50ZXJhY3Rpb25fcmVzdWx0X3RhYmxlID0gbW9kZWxfc3VtbWFyeShzdGRfbW50ZF9pbnRlcmFjdGlvbl9yZXN1bHQpCnN0ZF9tbnRkX2ludGVyYWN0aW9uX3Jlc3VsdF90YWJsZQpgYGAKCmBgYHtyfQpzdGRfbW50ZF9pbnRlcmFjdGlvbl9wcmVkX3Bsb3QgPSBwbG90X2RyZWRnZV9pbnRlcmFjdGlvbl9yZXN1bHQoc3RkX21udGRfaW50ZXJhY3Rpb25fcmVzdWx0X3RhYmxlKQpzdGRfbW50ZF9pbnRlcmFjdGlvbl9wcmVkX3Bsb3QKZ2dzYXZlKGZpbGVuYW1lKEZJR1VSRVNfT1VUUFVUX0RJUiwgJ2ZpZ3VyZTRfc3RkX21udGRfaW50ZXJhY3Rpb24uanBnJykpCmBgYAoKIyMjIE5vcm1hbGlzZWQKYGBge3J9Cm5ybV9tbnRkX2ludGVyYWN0aW9uX2Zvcm11bGEgPSBjcmVhdGVfaW50ZXJhY3Rpb25fZm9ybXVsYSgnbW50ZF9ub3JtYWwnKQpucm1fbW50ZF9pbnRlcmFjdGlvbl9yZXN1bHQgPSBtb2RlbF9hdmVyYWdlKG5ybV9tbnRkX2ludGVyYWN0aW9uX2Zvcm11bGEsIG5ybV9tbnRkX2FuYWx5c2lzX2RhdGEpCm5ybV9tbnRkX2ludGVyYWN0aW9uX3Jlc3VsdF90YWJsZSA9IG1vZGVsX3N1bW1hcnkobnJtX21udGRfaW50ZXJhY3Rpb25fcmVzdWx0KQpucm1fbW50ZF9pbnRlcmFjdGlvbl9yZXN1bHRfdGFibGUKYGBgCgpgYGB7cn0KbnJtX21udGRfaW50ZXJhY3Rpb25fcHJlZF9wbG90ID0gcGxvdF9kcmVkZ2VfaW50ZXJhY3Rpb25fcmVzdWx0KG5ybV9tbnRkX2ludGVyYWN0aW9uX3Jlc3VsdF90YWJsZSkKbnJtX21udGRfaW50ZXJhY3Rpb25fcHJlZF9wbG90Cmdnc2F2ZShmaWxlbmFtZShGSUdVUkVTX09VVFBVVF9ESVIsICdmaWd1cmU0X25ybV9tbnRkX2ludGVyYWN0aW9uLmpwZycpKQpgYGAKCgojIyBCZWFrIFdpZHRoCgojIyMgU3RhbmRhcmRpc2VkCmBgYHtyfQpzdGRfZ2FwZV9pbnRlcmFjdGlvbl9mb3JtdWxhID0gY3JlYXRlX2ludGVyYWN0aW9uX2Zvcm11bGEoJ2JlYWtfd2lkdGhfZmRpdl9zdGFuZGFyZCcpCnN0ZF9nYXBlX2ludGVyYWN0aW9uX3Jlc3VsdCA9IG1vZGVsX2F2ZXJhZ2Uoc3RkX2dhcGVfaW50ZXJhY3Rpb25fZm9ybXVsYSwgc3RkX2dhcGVfZmRpdl9hbmFseXNpc19kYXRhKQpzdGRfZ2FwZV9pbnRlcmFjdGlvbl9yZXN1bHRfdGFibGUgPSBtb2RlbF9zdW1tYXJ5KHN0ZF9nYXBlX2ludGVyYWN0aW9uX3Jlc3VsdCkKc3RkX21udGRfaW50ZXJhY3Rpb25fcmVzdWx0X3RhYmxlCmBgYAoKYGBge3J9CnN0ZF9nYXBlX2ludGVyYWN0aW9uX3ByZWRfcGxvdCA9IHBsb3RfZHJlZGdlX2ludGVyYWN0aW9uX3Jlc3VsdChzdGRfZ2FwZV9pbnRlcmFjdGlvbl9yZXN1bHRfdGFibGUpCnN0ZF9nYXBlX2ludGVyYWN0aW9uX3ByZWRfcGxvdApnZ3NhdmUoZmlsZW5hbWUoRklHVVJFU19PVVRQVVRfRElSLCAnZmlndXJlNF9zdGRfYmVha193aWR0aF9pbnRlcmFjdGlvbi5qcGcnKSkKYGBgCgojIyMgTm9ybWFsaXNlZApgYGB7cn0KbnJtX2dhcGVfaW50ZXJhY3Rpb25fZm9ybXVsYSA9IGNyZWF0ZV9pbnRlcmFjdGlvbl9mb3JtdWxhKCdiZWFrX3dpZHRoX2ZkaXZfbm9ybWFsJykKbnJtX2dhcGVfaW50ZXJhY3Rpb25fcmVzdWx0ID0gbW9kZWxfYXZlcmFnZShucm1fZ2FwZV9pbnRlcmFjdGlvbl9mb3JtdWxhLCBucm1fZ2FwZV9mZGl2X2FuYWx5c2lzX2RhdGEpCm5ybV9nYXBlX2ludGVyYWN0aW9uX3Jlc3VsdF90YWJsZSA9IG1vZGVsX3N1bW1hcnkobnJtX2dhcGVfaW50ZXJhY3Rpb25fcmVzdWx0KQpucm1fZ2FwZV9pbnRlcmFjdGlvbl9yZXN1bHRfdGFibGUKYGBgCgpgYGB7cn0KbnJtX2dhcGVfaW50ZXJhY3Rpb25fcHJlZF9wbG90ID0gcGxvdF9kcmVkZ2VfaW50ZXJhY3Rpb25fcmVzdWx0KG5ybV9nYXBlX2ludGVyYWN0aW9uX3Jlc3VsdF90YWJsZSkKbnJtX2dhcGVfaW50ZXJhY3Rpb25fcHJlZF9wbG90Cmdnc2F2ZShmaWxlbmFtZShGSUdVUkVTX09VVFBVVF9ESVIsICdmaWd1cmU0X25ybV9iZWFrX3dpZHRoX2ludGVyYWN0aW9uLmpwZycpKQpgYGAKCiMjIEhXSQoKIyMjIFN0YW5kYXJkaXNlZApgYGB7cn0Kc3RkX2h3aV9pbnRlcmFjdGlvbl9mb3JtdWxhID0gY3JlYXRlX2ludGVyYWN0aW9uX2Zvcm11bGEoJ2h3aV9mZGl2X3N0YW5kYXJkJykKc3RkX2h3aV9pbnRlcmFjdGlvbl9yZXN1bHQgPSBtb2RlbF9hdmVyYWdlKHN0ZF9od2lfaW50ZXJhY3Rpb25fZm9ybXVsYSwgc3RkX2h3aV9mZGl2X2FuYWx5c2lzX2RhdGEpCnN0ZF9od2lfaW50ZXJhY3Rpb25fcmVzdWx0X3RhYmxlID0gbW9kZWxfc3VtbWFyeShzdGRfaHdpX2ludGVyYWN0aW9uX3Jlc3VsdCkKc3RkX2h3aV9pbnRlcmFjdGlvbl9yZXN1bHRfdGFibGUKYGBgCgpgYGB7cn0Kc3RkX2h3aV9pbnRlcmFjdGlvbl9wcmVkX3Bsb3QgPSBwbG90X2RyZWRnZV9pbnRlcmFjdGlvbl9yZXN1bHQoc3RkX2h3aV9pbnRlcmFjdGlvbl9yZXN1bHRfdGFibGUpCnN0ZF9od2lfaW50ZXJhY3Rpb25fcHJlZF9wbG90Cmdnc2F2ZShmaWxlbmFtZShGSUdVUkVTX09VVFBVVF9ESVIsICdmaWd1cmU0X3N0ZF9od2lfaW50ZXJhY3Rpb24uanBnJykpCmBgYAoKIyMjIE5vcm1hbGlzZWQKYGBge3J9Cm5ybV9od2lfaW50ZXJhY3Rpb25fZm9ybXVsYSA9IGNyZWF0ZV9pbnRlcmFjdGlvbl9mb3JtdWxhKCdod2lfZmRpdl9ub3JtYWwnKQpucm1faHdpX2ludGVyYWN0aW9uX3Jlc3VsdCA9IG1vZGVsX2F2ZXJhZ2UobnJtX2h3aV9pbnRlcmFjdGlvbl9mb3JtdWxhLCBucm1faHdpX2ZkaXZfYW5hbHlzaXNfZGF0YSkKbnJtX2h3aV9pbnRlcmFjdGlvbl9yZXN1bHRfdGFibGUgPSBtb2RlbF9zdW1tYXJ5KG5ybV9od2lfaW50ZXJhY3Rpb25fcmVzdWx0KQpucm1faHdpX2ludGVyYWN0aW9uX3Jlc3VsdF90YWJsZQpgYGAKCmBgYHtyfQpucm1faHdpX2ludGVyYWN0aW9uX3ByZWRfcGxvdCA9IHBsb3RfZHJlZGdlX2ludGVyYWN0aW9uX3Jlc3VsdChucm1faHdpX2ludGVyYWN0aW9uX3Jlc3VsdF90YWJsZSkKbnJtX2h3aV9pbnRlcmFjdGlvbl9wcmVkX3Bsb3QKZ2dzYXZlKGZpbGVuYW1lKEZJR1VSRVNfT1VUUFVUX0RJUiwgJ2ZpZ3VyZTRfbnJtX2h3aV9pbnRlcmFjdGlvbi5qcGcnKSkKYGBgCgoKIyMgTWFzcwoKIyMjIFN0YW5kYXJkaXNlZApgYGB7cn0Kc3RkX21hc3NfaW50ZXJhY3Rpb25fZm9ybXVsYSA9IGNyZWF0ZV9pbnRlcmFjdGlvbl9mb3JtdWxhKCdtYXNzX2ZkaXZfc3RhbmRhcmQnKQpzdGRfbWFzc19pbnRlcmFjdGlvbl9yZXN1bHQgPSBtb2RlbF9hdmVyYWdlKHN0ZF9tYXNzX2ludGVyYWN0aW9uX2Zvcm11bGEsIHN0ZF9tYXNzX2ZkaXZfYW5hbHlzaXNfZGF0YSkKc3RkX21hc3NfaW50ZXJhY3Rpb25fcmVzdWx0X3RhYmxlID0gbW9kZWxfc3VtbWFyeShzdGRfbWFzc19pbnRlcmFjdGlvbl9yZXN1bHQpCnN0ZF9tYXNzX2ludGVyYWN0aW9uX3Jlc3VsdF90YWJsZQpgYGAKCmBgYHtyfQpzdGRfbWFzc19pbnRlcmFjdGlvbl9wcmVkX3Bsb3QgPSBwbG90X2RyZWRnZV9pbnRlcmFjdGlvbl9yZXN1bHQoc3RkX21hc3NfaW50ZXJhY3Rpb25fcmVzdWx0X3RhYmxlKQpzdGRfbWFzc19pbnRlcmFjdGlvbl9wcmVkX3Bsb3QKZ2dzYXZlKGZpbGVuYW1lKEZJR1VSRVNfT1VUUFVUX0RJUiwgJ2ZpZ3VyZTRfc3RkX21hc3NfaW50ZXJhY3Rpb24uanBnJykpCmBgYAoKIyMjIE5vcm1hbGlzZWQKYGBge3J9Cm5ybV9tYXNzX2ludGVyYWN0aW9uX2Zvcm11bGEgPSBjcmVhdGVfaW50ZXJhY3Rpb25fZm9ybXVsYSgnbWFzc19mZGl2X25vcm1hbCcpCm5ybV9tYXNzX2ludGVyYWN0aW9uX3Jlc3VsdCA9IG1vZGVsX2F2ZXJhZ2UobnJtX21hc3NfaW50ZXJhY3Rpb25fZm9ybXVsYSwgbnJtX21hc3NfZmRpdl9hbmFseXNpc19kYXRhKQpucm1fbWFzc19pbnRlcmFjdGlvbl9yZXN1bHRfdGFibGUgPSBtb2RlbF9zdW1tYXJ5KG5ybV9tYXNzX2ludGVyYWN0aW9uX3Jlc3VsdCkKbnJtX21hc3NfaW50ZXJhY3Rpb25fcmVzdWx0X3RhYmxlCmBgYAoKYGBge3J9Cm5ybV9tYXNzX2ludGVyYWN0aW9uX3ByZWRfcGxvdCA9IHBsb3RfZHJlZGdlX2ludGVyYWN0aW9uX3Jlc3VsdChucm1fbWFzc19pbnRlcmFjdGlvbl9yZXN1bHRfdGFibGUpCm5ybV9tYXNzX2ludGVyYWN0aW9uX3ByZWRfcGxvdApnZ3NhdmUoZmlsZW5hbWUoRklHVVJFU19PVVRQVVRfRElSLCAnZmlndXJlNF9ucm1fbWFzc19pbnRlcmFjdGlvbi5qcGcnKSkKYGBgCgoKCgoKCg==